转载:JVM内存分配策略

概述

内存分配策略:

  • 优先分配到 Eden
  • 大对象直接进入老年代
  • 长期存活的对象将进入老年代
  • 空间分配担保
  • 动态对象年龄判断

JDK 版本是1.8

优先分配到 Eden

设置启动参数:

  1. -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
  • -verbose:gc、-XX:+PrintGCDetails:打印 GC 日志
  • -XX:+UseSerialGC:使用 Serial GC
  • -Xms:设置堆的最小内存
  • -Xmx:设置堆的最大内存
  • -Xmn:设置新生代内存大小
  • -XX:SurvivorRatio:设置 Eden 比例,默认是 8:1:1

代码:

  1. public class Test1 {
  2. private static final int _1MB = 1024 * 1024;
  3. public static void main(String[] args) {
  4. byte[] b1, b2, b3, b4;
  5. b1 = new byte[2 * _1MB];
  6. b2 = new byte[2 * _1MB];
  7. b3 = new byte[2 * _1MB];
  8. b4 = new byte[4 * _1MB];
  9. }
  10. }

打印信息如下:

  1. [GC (Allocation Failure) [DefNew: 7292K->557K(9216K), 0.0054873 secs] 7292K->6701K(19456K), 0.0055414 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
  2. Heap
  3. def new generation total 9216K, used 4735K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  4. eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  5. from space 1024K, 54% used [0x00000000ff500000, 0x00000000ff58b450, 0x00000000ff600000)
  6. to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  7. tenured generation total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  8. the space 10240K, 60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
  9. Metaspace used 2781K, capacity 4486K, committed 4864K, reserved 1056768K
  10. class space used 302K, capacity 386K, committed 512K, reserved 1048576K

根据控制台打印的信息,我们发现给 b4 分配内存的时候,Eden 已经占用了 6MB,剩余的空间已经不足以存下 b4,因此发生 MinorGC。GC 期间虚拟机又发现3个 2MB 大小的对象无法存入 Survivor 空间,因此通过分配担保机制提前转移到老年代。

大对象直接进入老年代

虚拟机提供 -XX:PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配。目的是避免 Eden 区及两个 Survivor 区之间发生大量的内存复制。 注意:这个参数只对 Serial 和 ParNew 两款收集器有效,Parallel Scanvenge 不认识这个参数所以一般不需要配置,如果遇到必须使用此参数的场合,可以考虑 ParNew 加 CMS 收集器的组合。

设置启动参数:

  1. -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728

代码:

  1. public class Test1 {
  2. private static final int _1MB = 1024 * 1024;
  3. public static void main(String[] args) {
  4. byte[] b1 = new byte[4 * _1MB];
  5. }
  6. }

打印信息如下:

  1. Heap
  2. def new generation total 9216K, used 1312K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  3. eden space 8192K, 16% used [0x00000000fec00000, 0x00000000fed48350, 0x00000000ff400000)
  4. from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  5. to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
  6. tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  7. the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
  8. Metaspace used 2781K, capacity 4486K, committed 4864K, reserved 1056768K
  9. class space used 302K, capacity 386K, committed 512K, reserved 1048576K

根据控制台打印的信息,Eden 空间使用了16%,老年代使用了40%,也就是 4MB 的 b1 对象直接进入了老年代。

长期存活对象将进入老年代

如果对象在 Eden 出生之后经过第一次 MinorGC 仍然存在,并且能被 Survivor 容纳的话,将被移动到 Survivor 中,并设定对象年龄为1。此后,对象在 Survivor 中每经过一次 Minor GC,年龄就增加1岁,当它的年龄增加到15岁时,就会被晋升到老年代中。这个晋升的阈值可以通过参数 -XX:MaxTenuringThreshold 设置,该参数默认值是15。

设置启动参数:

  1. -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution
  • -XX:MaxTenuringThreshold=1:设置最大年龄是1的就晋升到老年代中;
  • -XX:+PrintTenuringDistribution:指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布;

动态对象年龄判定

为了适应不同程序的内存状况,虚拟机并不是永远的要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄对象就可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

空间分配担保

在发生 Minor GC 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么 Minor GC 可以确保是安全的。如果不成立,虚拟机则会查看 HandlePromotionFailure 设置值是否允许担保失败。如果允许,则会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于将尝试着进行一次 Minor GC,尽管这次 GC 是有风险的。如果小于,或者 HandlePromotionFailure 设置不允许冒险,则会进行 Full GC。

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/dl5g73 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。