【JVM系列】内存分配与回收策略详解


Minor GC 和 Full GC

  • Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。

  • Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。

内存分配策略

当我们new一个对象实例时,首先是存入堆中新生代中的伊甸园区,如果伊甸园区空间满了,就会进行YGC,本篇文章就讲述一下对象的分配过程是如何的…

一般过程

第一次轻GC

image-20210615215811323.png

  1. new的对象先放着伊甸园区;
  2. 当伊甸园区空间满了时,程序又要创建新的对象时,JVM垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区的不再被其他对象所引用的对象进行销毁,再加载新的对象到伊甸园区;
  3. 然后将伊甸园区剩余的对象移动到幸存者0区,每个对象被分配了一个年龄计数器(age),每进行一次GC,幸存下来的对象age累加,上面的两个幸存对象age被赋值为1;
  4. GC完成后,此时伊甸园区是空的了。

说明:

  1. S0区和S1区,也被叫做From区和To区,判断二者很简单,谁是空的谁是To,则另外一个就是From区;
  2. 每次GC时,幸存的对象则会被放入To区,以上面的为例,第一次GC时,S0是To区,当GC完成后,S1区是空的就变成了To区;

第二次轻GC

image-20210615221650327.png

  1. 当伊甸园区空间又满了时,进行第二次GC,将伊甸园区幸存的对象放入S1区,age此时为1;

  2. 同时对S0区的对象进行判断是否还被使用,如果被使用的话,就将其放入S1区,age累加此时为2;

  3. 第二次GC完成后,S0区为空了,此时S0区为To区,S1区为From区;

第N次GC

image-20210615223737583.png

  1. 第N次GC时,我们发现S1区的有两个对象的age已经是15了,如果此时这两个对象还是被引用的,则将其晋升到Old区;
  2. 其中15为默认阈值,这个阈值是可以自己设置:-XX:MaxTenuringThreshold=<N>

特殊过程

image-20210615232416390.png

  1. 创建一个新的对象时,首先判断Eden区是否放的下,如果放的下,就为其分配内存,放不下的话,就进行YGC;
  2. 然后再判断Eden区是否放的下,如果此时放的下的话,就为其分配内存,如果还是放不下,说明这个对象比伊甸园区的空间还要大,这个对象是个超大对象,此时将其放入Old区,如果Old区也放不下,就进行FGC,然后再判断Old区能不能放下,放的下就存在Old区,如果还是放不下,就会出现OOM异常;
  3. 在进行YGC的时候,幸存的对象会放入幸存区,此时判断是否能放下,如果幸存区放不下,就会直接放入Old区。

小结

  1. 什么时候进行GC呢?

    • 当伊甸园区满的时候才会进行GC,幸存区满的时候不会进行GC,只有当伊甸园区满的时候,幸存区才会被动进行GC。
  2. 关于幸存者S0和S1区:复制之后有交换,谁空谁是To;

  3. 对于垃圾回收:频繁在新生区进行回收,很少在老年区收集,几乎不在永久区/元空间收集。

Full GC 的触发条件

对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件:

1. 调用 System.gc()

只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。

2. 老年代空间不足

老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。

为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。

3. 空间分配担保失败

使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。具体内容请参考上面的第 5 小节。

4. JDK 1.7 及以前的永久代空间不足

在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。

当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。

为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。

5. Concurrent Mode Failure

执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。

猜你喜欢

转载自blog.csdn.net/jiang_wang01/article/details/131173505