JVM内存分配策略(对象动态年龄机制与空间分配担保机制)

内存分配策略(或对象提升(Promotion)规则)

  • 如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor区中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,其实每个M、每个GC都有所不同)时,就会被晋升到老年代中。
  • 对象晋升老年代的年龄阈值,可以通过选项 -XX:MaxTenuringThreshold来设置。

补充关于jdk7与jdk8的分配区别

  • 在jdk7中,分配新对象空间不足时,会将在Eden存活的对象移入老年代空出Eden的空间给新对象使用。
  • 在jdk8中,分配新对象大于Eden剩余部分时候会直接进入老年代。

针对不同年龄段的对象分配原则如下所示:

  • 优先分配到Eden
  • 大对象直接分配到老年代
    ——>>尽量避免程序中出现过多的大对象
  • 长期存活的对象分配到老年代
  • 动态对象年龄判断
    ——>>如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
  • 空间分配担保
    -XX:HandlePromotionFailure

为对象分配内存:TLAB

为什么有TLAB(Thread Local Allocation Buffer)?

  • 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据
  • 由于对象实例的创建在JM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的
  • 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

什么是TLAB?

  • 从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。
  • 多线程同时分配内存时,使用T工AB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略

据我所知所有openJDK衍生出来的JVM都提供了TLAB的设计。

TLAB的补充说明:

尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选
在程序中,开发人员可以通过选项“-XX:UseTLAB”设置是否开启TLAB空间。
默认情况下,T工AB空间的内存非常小,仅占有整个Eden空间的1%,当然我们可以通过选项“-XX:TLABWasteTargetPercent”设置TLAB空间所占用Eden空间的百分比大小。
一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存。

对象分配过程:TLAB

image-1668333974183

对象动态年龄机制

当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总大小大于这Survivor区域内存大小的 50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了。
例如:
Survivor区域里现在有一批对象,年龄1 + 年龄2 +…+年龄n 多个年龄对象总和超过了Survivor区域的50%,此时就会 把年龄n(含)以上 的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。
对象动态年龄判断机制一般是在minor gc之后触发的。

空间分配担保机制

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

  • 如果大于,则此次Minor GC是安全的
  • 如果小于,则虚拟机会查看-X:HandlePromotionFailure设置值是否允许担保失败。
    —— 如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小。
    ————> 如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的:
    ————> 如果小于,则改为进行一次Full GC。
    —— 如果HandlePromotionFailure=false,则改为进行一次Full GC。

在JDK6 Update24之后,HandlePromotionFailure参数不会再影响到虚拟机的空
间分配担保策略,观察openJDK中的源码变化,虽然源码中还定义了
HandlePromotionFailure参数,但是在代码中已经不会再使用它。JDK6 Update 24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者大于历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

猜你喜欢

转载自blog.csdn.net/qq_49619863/article/details/127837482
今日推荐