内存分配
之前讲了垃圾回收器体系以及运作原理,现在来看看对象内存分配那点事儿。对象的内存分配,往大方向讲就是在堆上分配,对象主要分配在新生代的Eden区上,也可能直接分配在老年代中,并不固定,取决于使用的哪一种垃圾收集器以及虚拟机参数设置。
对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机会发一起一次Minor GC。
不同的垃圾收集器组合对于对象的分配是有影响的,我们这里都是测试在Serial+SerialOld
的收集器组合下测试的代码。
下面的代码,-Xms20M -Xmx20M -Xmn10M
三个参数限制了Java堆大小为20M,不可扩展,分给新生代10M,剩下10M分给老年代,-XX:SurvivorRatio=8
定义了Eden区与一个Survivor区的空间比例是8:1,-XX:+UseSerialGC
参数指定Serial垃圾收集器
|
|
GC输出:
|
|
我们可以看到eden space
是8M,前面3个对象都分配到了eden区,在分配allocation4
的时候,eden区已经不够了,于是发生了一次Minor GC,但是3个对象都是存活的,并且无法放进Survivor(from space)区,所以通过分配担保机制转移到了老年代去。然后4M的allocation4
分配进了Eden区。
大对象直接进入老年代
虚拟机提供了一个-XX:PretenureSizeThreshold参数,大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden区以及两个Survivor区之间发生大量的复制(新生代采用复制算法)。
-XX:PretenureSizeThreshold只在Serial和ParNew两款收集器有效。
|
|
运行结果:
|
|
看到对象超过了3M,直接进入了tenured generation(老年代)。
长期存活的对象将进入老年代
对象在Eden区每gc留下来一次(大小可复制到Survivor区中),年龄+1,默认是15岁后移到老年代。这个阀值可以通过-XX:MaxTenuringThreshold设置。
|
|
运行结果:
|
|
为了适应不同程序的内存状况,Survivor空间中相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到“-XX:MaxTenuringThreshold”设置要求的年龄。