JVM垃圾回收机制!

GC类型

Minor GC(Young GC):

新生代收集( Minor GC/Young GC ):

  • 当年轻代空间不足时,就会触发Minor GC。

整个年轻代中又可以分为Eden区和survivor区,survivor区又分为survivor0和survivor1。

  • Eden区和Survivor0区和Survivor1区,之间的配比是8:1:1(默认情况下)

年轻代GC回收过程:

  • 当eden区被对象塞满后就会发生GC。

  • 在eden区中筛选出来有用的对象,把有用的对象复制到survivor0/survivor1

    • 第一次随机分配,假设分配到survivor0,剩下eden区的对象都是垃圾对象,直接干掉。
  • 程序继续执行,eden区的对象又放不下了,又触发Minor GC,这次它不仅会回收eden区,还会回收survivor0区的垃圾对象。

    • 把eden区和survivor0区的有用对象一起放到survivor1区,eden区和survivor0区剩余的垃圾对象直接干掉。
  • 程序继续执行,eden区的对象又放不下了,又会触发Minor GC,这次它不仅会回收eden区,还会回收survivor1区的垃圾对象,把eden区和survivor1区的引用对象一起放到survivor0区,eden区和survivor1区剩余的垃圾对象直接干掉。

  • 依次往复循环,直到超过了一定的次数,对象就会放到老年代区,老年代放不下就要执行Full GC了。

老年代收集(Major GC/Old GC):老年代的垃圾收集。

在老年代空间不足时,则触发Major GC。

对象什么情况会进入老年代?

大对象:

需要大量连续内存空间的对象,最典型的大对象就是那种很长的字符串以及数组。

长期存活的对象:

虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,对象在Survivor区中每熬过一次Minor GC,年龄就增加1。

当他的年龄增加到一定程度(默认是15岁), 就将会被晋升到老年代中。

  • 对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

动态对象年龄判定:

如果在Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半。

年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

空间分配担保:

在一次安全Minor GC 中,存活的对象不能在另一个Survivor 完全容纳,则会通过担保机制进入老年代。

什么样的对象会被回收?

引用计数法:

给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就+1。

当引用失效时,计数器值就-1,任何时刻计数器为0的对象就是不可能在被使用。

无法解决对象之间互相引用的情况。

  • A到B,B到C,C到A,相互引用,但是他们垃圾的话,用引用计数是无法被回收的。

可达性分析算法:

通过GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。

当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时。

则证明此对象是不可用的,这些对象就会被当作垃圾从而被回收掉的。

哪些是GCRoots对象的根:

虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象

方法区中的类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中(Native方法)引用的对象

触发Full GC执行的情况?

调用System.gc()时,系统建议执行Full GC,但是不必然执行。

老年代空间不足。

方法区空间不足。

通过Minor GC后进入老年代的平均大小 大于 老年代的可用内存。

  • Eden、Survivor Space0(From Space)区 向Survivor Space1(To Space)区复制时。
    • 对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存 小于 该对象大小。

Full GC问题排查

查看GC日志:

  • 通过jstat -gcutil -t pid 1000 1000查看GC日志。

查看堆内存情况:

  • jmap -heap pid查看堆内存情况。

查看哪些类占用的空间多:

  • 通过jmap -hsito pid查看哪些类占用的空间多。

查看堆内存日志:

  • 通过jmap -dump:format=b,file=xxxx.hprofDump查看堆内存日志。
  • 通过MAT内存分析工具分析日志。

空间分配担保机制

在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间 是否大于 新生代所有对象的总空间。

  • 如果大于,则此次Minor GC是安全的。

  • 如果小于,则虚拟机会查看 -XX:HandlePromotionFailure 设置值是否允许担保失败。

如果HandlePromotionFailure=true,那么会继续 检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小。

  • 如果大于,则尝试进行一次Minor GC。

  • 如果小于,则进行一次Full GC。

如果HandlePromotionFailure=false,则进行一次Full GC。

image-20231021164242524