3.8.1 对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
HotSpot虚拟机提供了-XX:+PrintGCDetails
这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。在实际的问题排查中,收集器日志常会打印到文件后通过工具进行分析,不过本节实验的日志并不多,直接阅读就能看得很清楚。
在代码清单3-7的testAllocation()方法中,尝试分配三个2MB大小和一个4MB大小的对象,在运行时通过-Xms20M、-Xmx20M、-Xmn10M这三个参数限制了Java堆大小为20MB,不可扩展,其中10MB分配给新生代,剩下的10MB分配给老年代。-XX:Survivor-Ratio=8
决定了新生代中Eden区与一个Survivor区的空间比例是8∶1,从输出的结果也清晰地看到“eden space 8192K、from space 1024K、to space 1024K”的信息,新生代总可用空间为9216KB(Eden区+1个Survivor区的总容量)。
执行testAllocation()中分配allocation4对象的语句时会发生一次Minor GC,这次回收的结果是新生代6651KB变为148KB,而总内存占用量则几乎没有减少(因为allocation1、2、3三个对象都是存活的,虚拟机几乎没有找到可回收的对象)。产生这次垃圾收集的原因是为allocation4分配内存时,发现Eden已经被占用了6MB,剩余空间已不足以分配allocation4所需的4MB内存,因此发生Minor GC。垃圾收集期间虚拟机又发现已有的三个2MB大小的对象全部无法放入Survivor空间(Survivor空间只有1MB大小),所以只好通过分配担保机制提前转移到老年代去。
这次收集结束后,4MB的allocation4对象顺利分配在Eden中。因此程序执行完的结果是Eden占用4MB(被allocation4占用),Survivor空闲,老年代被占用6MB(被allocation1、2、3占用)。通过GC 日志可以证实这一点。
代码清单3-7 新生代Minor GC
1 | private static final int _1MB = 1024 * 1024; |
运行结果:
1 | [GC [DefNew: 6651K->148K(9216K), 0.0070106 secs] 6651K->6292K(19456K), 0.0070426 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] |