系统相关
首页 > 系统相关> > JVM内存分配与回收机制

JVM内存分配与回收机制

作者:互联网

Minor GC 和 Full GC

对象优先在Eden区分配

 

大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

先看一个示例:

jvm参数设置:-Xms512m -Xmx512m -XX:+PrintGCDetails

如图所示,创建了两个 byte 数组,各自 60000KB,都是先被分配到 eden 区的。

再给 arr3 分配 60000KB 空间看下效果:

因为给 arr3 分配内存的时候,eden 区内存几乎已经被分配完了,而当 eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,GC 期间虚拟机又发现 arr1 和 arr 2 无法存入 from 和 to 空间(这俩太小了,没法存放 60000KB 的对象),所以只好把新生代的对象提前转移到老年代中去,老年代上的空间足够存放 arr1 和 arr2,所以不会出现 Full GC。执行 Minor GC 后,后面分配的对象如果能够存在 eden 区的话,还是会在 eden 区分配内存,所以 arr3 被分配在了 eden 区。

Minor gc 后存活的对象 Survivor 区放不下

通过上面的例子也可以看出,如果 survivor 区放不下,会直接放到老年代中(部分可能还会放在Survivor 区)。

Eden 区 和 Survivor 区比例

eden:from:to ,默认是 8:1:1,可以通过设置 -XX:SurvivorRatio 参数来修改。

虽然说是默认 8:1:1,但是 jvm 也会自动调整的。-XX:+UseAdaptiveSizePolicy 参数开启时,jvm 会自动调整这个比例(经常看到的是 6:1:1)。如果不想这个比例有变化可以设置参数 -XX:-UseAdaptiveSizePolicy。

大量的对象被分配在 eden 区,eden 区满了后会触发 Minor GC,可能会有99%以上的对象成为垃圾被回收掉,剩余存活的对象会被挪到为空的那块 survivor 区。下一次 eden 区满了后又会触发 Minor GC,把 eden 区和 survivor 区的垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的 survivor 区。

因为新生代的对象都是朝生夕死的,存活时间很短,所以 JVM 默认的 8:1:1 的比例是很合适的,让 eden 区尽量的大,survivor 区够用即可。这个可以根据业务实际情况进行调整。

大对象直接进入老年代

大对象就是需要大量连续内存空间的对象。

JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小,会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和 ParNew 两个收集器下有效。

-XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000000 -XX:+UseSerialGC

可以看到,虽然 eden 区足够放这个对象,但是它还是进入了老年代,因为它超过了 PretenureSizeThreshold 设置的大小,所以直接进入老年代了。这是为了避免为大对象分配内存时的复制操作而降低效率。

长期存活的对象会进入老年代

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。

为了做到这一点,虚拟机给每个对象一个对象年龄计数器

  1. 如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。
  2. 对象在 Survivor 中每熬过一次 Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。

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

对象动态年龄判断

当前放对象的 Survivor 区域里(其中一块区域,放对象的那块 Survivor 区),一批对象的总大小大于这块 Survivor 区域内存大小的50%,那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。

例如 Survivor 区域里现在有一批对象,年龄1 + 年龄2 + 年龄n 的多个年龄对象总和超过了 Survivor 区域的 50%,此时就会把年龄 n 以上的对象都放入老年代。

这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在 Minor GC 之后触发的。

老年代空间分配担保机制

年轻代每次 Minor GC 之前,JVM 会计算下老年代最大可用的连续空间。

如果这个连续空间大于年轻代里现有的所有对象大小之和(包括垃圾对象),或者大于历次 Minor GC 的平均晋升大小,那么会进行 Minor GC。否则,会进行 Full GC。

如果 Minor GC 之后剩余存活的需要挪动到老年代的对象,老年代吃不下,还是会触发 Full GC。所以,在 Minor GC 之前,先估算下老年代能不能吃下 Minor GC 剩余的对象,如果不行,直接进行 Full GC,这样也省去了 Minor GC 的时间。

标签:Survivor,eden,对象,回收,XX,GC,内存,JVM,Minor
来源: https://blog.csdn.net/xujingyiss/article/details/121954037