JAVA基础 - JVM(虚拟机)

概念

Java是一门抽象程度特别高的语言,提供了自动内存管理等一系列的特性。这些特性直接在操作系统上实现是不太可能的,所以就需要JVM进行一番转换。

https://pics5.baidu.com/feed/bd315c6034a85edf2970b8fa3a8d1a2adf547549.png@f_auto?token=022e6ceaf57400ff6f6684ad662f65c2

从图中可以看到,有了JVM这个抽象层之后,Java就可以实现跨平台了。JVM只需要保证能够正确执行.class文件,就可以运行在诸如Linux、Windows、MacOS等平台上了。

内存模型

五大区

JVM的内存结构包括五大区域:

  1. 程序计数器(线程私有,无GC):指向当前线程正在执行的字节码的地址、行号。
  2. 虚拟机栈(线程私有,无GC):存储当前线程运行方法所需要的局部变量、操作栈等。每一个方法都对应一个栈帧,可以通过配置Xss来配置栈帧的大小
  3. 本地方法栈(线程私有,无GC):和虚拟机栈类似,不同的是,本地方法栈存储的是本地方法的数据。
  4. 方法区(线程共享,要GC):被所有方法线程共享的一块内存区域。用于存储已经被JVM加载的类信息、常量、静态变量等。这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。通过永久代(Java8之前)或者元空间来实现。
  5. 堆区(线程共享,要GC):被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。一个JVM实例只有一个堆内存。

        总结

  • 程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。
  • 堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分

Heap(堆内存)

         参考资料:JVM系列(二) Java 堆内存分析_java堆内存比例-优快云博客

堆(Heap)是GC执行垃圾回收的重点区域,如果出现OOM或者出现内存泄露,一定是出在堆内存上,因为堆是JVM中最大的一块内存空间,所有线程共享Java堆,物理上不连续的逻辑上连续的内存空间,几乎所有的实例都在这里分配内存,在方法结束后,堆中的对象不会马上删除,仅仅在垃圾收集的时候被删除

Java7及以前将JVM空间逻辑上分成三部分::

  1. 年轻代(YoungGen)年轻代分为Eden(生成区)和Survivor(幸存区),大小比例默认为8:2;其中Survivor又分为s0(From Space)和s1(To Space)。这两个空间大小是一模一样的,就是一对双胞胎,他俩是1:1的比例;
  2. 老年代(OldGen)较大的对象数据,年轻代存不下会放入老年代;存活很久,没有被清除掉的对象也会放入老年代。
  3. 永久代(PermGen)也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。

        Java8废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现。他们最大区别是:元空间并不在JVM中,而是使用本地内存。

        

OOM(内存溢出)

第一种:OutOfMemoryError: Java heap space

发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:

1、检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。

2、增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms4G –Xmx4G

第二种: java.lang.OutOfMemoryError: Metaspace

发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Metaspace有关。解决这类问题有以下两种办法:

1、增加java虚拟机中的XX:MetaspaceSize和XX:MaxMetaspaceSize参数的大小,其中XX:MetaspaceSize是初始元数据区域大小,XX:MaxMetaspaceSize是最大元数据区域大小。

2、JAVA_OPTS=" -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=1024m"

第三种:OutOfMemoryError:unable to create new nativethread

一般由于两个原因导致的:

1)内存空间不足以满足创建线程所需的stack size
  virtual memory < stack size*the number of threads

2)线程数已达到操作系统的上限

内存分析工具

  • MAT(Memory Analyzer Tool)
  • File-Tool

JVM参数

参考资料:面试必问之JVM常用参数_maxheapsize-优快云博客

参数

描述

-Xms (-XX:InitialHeapSize)

堆内存初始大小,默认为物理内存1/64

-Xmx (-XX:MaxHeapSize)

堆的最大分配内存,默认为物理内存1/4。

在很多情况下,通常将-Xms和-Xmx设置成一样的,因为当堆不够用而发生扩容时,会发生内存抖动影响程序运行时的稳定性。

-Xss (-XX:ThreadStackSize)

规定了每个线程虚拟机栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。

-XX:MetaspaceSize

方法区(元空间)内存大小,一般初始化200m,最大1024m就够了

-XX:MaxMetaspaceSize

方法区(元空间)内存最大允许大小。元空间并不在虚拟机中,而是使用本地内存,因此默认情况下,元空间的大小仅受本地内存限制。

-XX:NewSize(-Xns

年轻代内存初始大小

-XX:MaxNewSize(-Xmn

年轻代内存最大允许大小

-XX:SurvivorRatio

年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1:1

-XX:NewRatio

设置年轻代和年老代的比值。默认值为2,表示年轻代和年老代比值为1 : 2

-XX:+PrintGC

jvm启动后,只要遇到GC就会打印日志

-XX:+PrintGCDetails

输出详细Gc收集日志信息

-XX:+DisableExplicitGC

关闭System.gc()

-XX:+CollectGen0First

FullGC时是否先YGC,默认false

-XX:MaxTenuringThreshold

设定对象在Survivor复制的最大年龄阈值,超过阈值转移到老年代。

-XX:PretenureSizeThreshold

当创建的对象超过指定大小时,直接把对象分配在老年代。

GC (Garbage Collection)

        参考资料:Java GC、Full GC_java full gc-优快云博客

概念

GC(Garbage Collection)是JVM自动管理内存的一项重要功能,它负责在运行时自动回收不再被使用的对象,并释放它们占用的内存空间。Java的GC系统通过以下几个步骤来执行垃圾回收:

  1. 标记(Mark):GC系统首先标记所有活跃对象,即那些仍然被引用的对象。它从根对象(如线程栈、静态变量)开始遍历对象图,并将活跃对象进行标记。
  2. 清除(Sweep):在标记阶段之后,GC系统会清除所有未标记的对象,即那些不再被引用的对象。清除的对象会被认为是垃圾,其占用的内存将被释放。
  3. 压缩(Compact):在清除阶段之后,GC系统可能会进行内存压缩操作。内存压缩的目的是为了消除内存碎片化,将存活对象紧凑地排列在一起,以便为新对象分配连续的内存空间。

GC系统采用了不同的垃圾回收算法和垃圾收集器,可以根据应用程序的需求和硬件环境选择适合的组合。常见的垃圾回收算法有标记-清除算法、复制算法、标记-整理算法等。

GC回收对象过程

第一步,新生成的对象首先放到Eden区,当Eden区满了会触发Minor GC。

第二步,第一步GC活下来的对象,会被移动到survivor区中的S0区,S0区满了之后会触发Minor GC,S0区存活下来的对象会被移动到S1区,S0区空闲。

S1满了之后在GC,存活下来的再次移动到S0区,S1区空闲,这样反反复复GC,每GC一次,对象的年龄就涨一岁,达到某个值后(15),就会进入老年代。

第三步,在发生一次Minor GC后(前提条件),老年代可能会出现Major GC,这个视垃圾回收器而定。

Young GC(Minor GC)

         Young GC又称Minor GC,对新生代进行GC。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。Young GC同样会会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

Full GC(Major GC)

Full GC(Full Garbage Collection)是Java虚拟机对整个堆内存进行垃圾回收的过程,包括新生代和老年代的垃圾回收。Full GC的速度一般会比Young GC慢10倍以上,STW的时间更长,如果Full GC 后,内存还不足,就报OOM了。

触发Full GC 执行的情况有以下几种:

  1. 老年代空间不足:当老年代中的对象占用的空间超过了老年代的阈值时,会触发Full GC来回收老年代中的垃圾对象。
  2. 方法区(永久代)空间不足:在Java7之前的版本中,永久代(PermGen)用于存储类信息和常量池等数据。如果永久代空间不足,也会触发Full GC来回收永久代中的垃圾对象。
  3. 手动调用:通过调用System.gc()方法或者使用垃圾收集器的API触发Full GC。
  4. 垃圾收集器策略变更:在切换垃圾收集器或者调整垃圾收集器的参数时,可能会触发Full GC来完成垃圾收集器的切换或者参数的调整。
  5. 内存分配失败:当Java虚拟机尝试分配新对象时,但堆内存已经无法分配足够的空间时,可能会触发Full GC。
  6. JVM启动和关闭阶段:在JVM启动和关闭过程中,可能会执行Full GC来清理未完成的垃圾回收任务。

请注意,Full GC的发生是Java虚拟机自动管理内存的一部分,开发人员无法直接控制或干预。优化应用程序的内存使用和垃圾回收策略,可以减少Full GC的频率和影响。

GC优化措施

优化GC的目标是减少GC的频率和时间,以提高应用程序的性能和稳定性。下面是一些优化GC的常见策略:

  1. 堆内存调优:通过适当调整堆内存大小来减少Full GC的频率。可以根据应用程序的内存需求和垃圾回收的特点,调整-Xms(初始堆大小)和-Xmx(最大堆大小)参数。
  2. 避免过度创建对象:频繁创建大量临时对象会增加Full GC的负担。可以通过使用对象池、复用对象、使用StringBuilder等手段来减少对象的创建和销毁。
  3. 注意对象引用的生命周期:及时释放不再使用的对象引用,帮助垃圾回收器判断对象是否可回收。尤其是在长时间运行的任务中,需要特别注意避免意外持有对象的引用,导致内存无法释放。
  4. 使用并行GC或G1收集器:Parallel GC和G1收集器在Full GC的性能方面有较好的表现。可以根据应用程序的需求和硬件环境选择合适的垃圾收集器。
  5. 减少Full GC的时间窗口Full GC通常会导致应用程序的停顿,影响用户体验。可以通过调整Full GC的时间窗口,将Full GC的发生时间尽量安排在业务低峰期。
  6. 监控和调优:通过监控GC日志、堆内存使用情况等指标,定位Full GC的原因和性能瓶颈,进行有针对性的调优。
  7. 使用内存分析工具:使用内存分析工具(如VisualVM、MAT等)来分析内存使用情况、对象引用关系等,帮助定位内存泄漏或过度使用的问题。

需要根据具体的应用程序和环境来选择合适的优化策略。对于复杂的应用程序,可能需要结合性能测试和调优实验,不断优化Full GC的配置和策略,以达到最佳性能和稳定性。

GC分类

参考资料:https://zhuanlan.zhihu.com/p/259740590

常见的七种经典垃圾收集器,按作用于不同分代分类:

新生代:Serial,ParNew,Parallel Scavenge;

老年代:Serial Old,Parallel Old,CMS;

新生代+老年代:G1

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/be08baa49b7a479489f29534915b7a0e~tplv-k3u1fbpfcp-zoom-1.image

上图中,新生代和老年代区域的回收器之间进行连线,说明他们之间可以搭配使用。

GC回收策略

常见内存回收策略可以从以下几个维度来理解:

  • 串行 & 并行

串行(Serial):单线程执行内存回收工作。十分简单,无需考虑同步等问题,但耗时较长,不适合多cpu。

并行(Parallel):多线程并发进行回收工作。适合多CPU,效率高。

  • 并发 &  stop the world

stop the world:jvm里的应用线程会挂起,只有垃圾回收线程在工作进行垃圾清理工作。简单,无需考虑回收不干净等问题。

并发(仅CMS支持):在垃圾回收的同时,应用也在跑。保证应用的响应时间。会存在回收不干净需要二次回收的情况。

  • 压缩&非压缩

压缩:在进行垃圾回收后,会通过滑动,把存活对象滑动到连续的空间里,清理碎片,保证剩余的空间是连续的。

非压缩:保留碎片,不进行压缩。

copy:将存活对象移到新空间,老空间全部释放(需要较大的内存)。

一个垃圾回收算法,可以从上面几个维度来考虑和设计,而最终产生拥有不同特性适合不同场景的垃圾回收器。

查看默认GC

在要查询的机器的命令行中输入以下命令:

java -XX:+PrintCommandLineFlags -version

得到的结果大抵如下所示:

-XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC

上面的-XX:+UseParallelGC ,表示的正是目前在使用的是Parallel垃圾收集器,如果-XX:+UseCompressedOops后面就没有输出结果了,则是Serial垃圾收集器。

如果默认是Serial垃圾收集器, jvm所在的机器的配置非常低。jdk并没有默认不变的垃圾收集器,jdk会根据所在的机器的环境,自动适配比较适合的垃圾收集器。

Serial 收集器
  1. 针对区域:新生代。
  2. 原理:使用标记复制算法。
  3. 优点:简单、高效(相对于其他单线程),额外内存消耗最小, 总体说来 Serial收集器对于运行在客户端模式(例如swing构建的应用)下的虚拟机来说是一个很好的选择。
  4. 缺点:垃圾收集停顿时间长。

Serial收集器是最基础、历史最悠久的收集器。迄今为止,它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器。

ParNew 收集器
  1. 针对区域:新生代。
  2. 原理Serial收集器的多线程并行版本,行为与Serial一致,使用标记复制算法,同时使用多条垃圾收集线程进行垃圾收集。
  3. 优点:除了Serial收集器外,只有它能与老年代收集器CMS收集器配合工作, 新生代收集器选用ParNew,老年代选用CMS是自jdk5以来到jdk8,官方一直推荐的服务器端的收集器最佳拍档。
  4. 缺点:自JDK 9开始,ParNew加CMS收集器的组合就不再是官方 推荐的服务端模式下的收集器解决方案了,ParNew可以说是HotSpot虚拟机中第一款退出历史舞台的垃圾收集器。
Parallel Scavenge 收集器
  1. 针对区域:新生代。
  2. 原理:是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。
  3. 优点:可控制的吞吐量,高吞吐量,高效利用 CPU,垃圾收集的自适应的调节策略。
  4. 缺点:自JDK 9开始,ParNew加CMS收集器的组合就不再是官方 推荐的服务端模式下的收集器解决方案了,ParNew可以说是HotSpot虚拟机中第一款退出历史舞台的垃圾收集器。

Serial Old 收集器
  1. 针对区域:老年代。
  2. 原理:使用标记-整理算法,它是Serial收集器的老年代版本,它同样是一个单线程收集器
  3. 用途:这个收集器的主要意义也是供客户端模式使用。如果在服务端模式下,它也可能有两种用途:一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用; 另外一种就是作为CMS 收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用。

Parallel Old 收集器
  1. 针对区域:老年代。
  2. 原理:使用标记-整理算法,它是Parallel Scavenge收集器的老年代版本,支持多线程并发收集。
  3. 用途:在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合。

CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。

  1. 针对区域:老年代。
  2. 使用:在使用参数-XX:+UseConcMarkSweepGC后,老年代启用CMS收集器, 而对应就会默认新生代收集器为ParNew。
  3. 原理:基于标记-清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤,分别是 1.初始标记, 2.并发标记, 3.重新标记, 4.并发清除。
  • 其中1.初始标记和 3.重新标记,这两个步骤仍然需要STW,但在整个过程中耗时最长的2.并发标记和4.并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说:CMS收集器的内存回收过程是与用户线程一起并发执行的

G1 收集器

Garbage First(简称G1)是一款在server端运行的垃圾收集器,专门针对于拥有多核处理器和大内存的机器,在JDK9中更被指定为官方GC收集器。它满足高吞吐量的同时满足GC停顿的时间尽可能短, 到了JDK 8 Update 40的时候,G1提供并发的类卸载的支持,补全了其计划功能的最后一块拼图。这个版本以后的G1收集器才被Oracle官方称为“全功能的垃圾收集器”(Fully-Featured Garbage Collector)。

在G1收集器出现之前的所有 其他收集器,包括CMS在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老 年代(Major GC),再要么就是整个Java堆(Full GC)。而G1跳出了这个樊笼,它可以面向堆内存任 何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而 是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值