内存分配与回收策略-实例
代码1-1 新生代 Minor GC
一、新生代 Minor GC
public class One { private static final int _1MB = 1024 * 1024; /** * VM参数:-XX:+UseSerialGC -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 */ public static void testAllocation() { byte[] allocation1, allocation2, allocation3, allocation4; allocation1 = new byte[2 * _1MB]; allocation2 = new byte[2 * _1MB]; allocation3 = new byte[2 * _1MB]; allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC } public static void main(String[] args) { testAllocation(); } }
[GC (Allocation Failure) [DefNew: 6304K->639K(9216K), 0.0028333 secs] 6304K->4735K(19456K), 0.0028708 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 7104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 78% used [0x00000000fec00000, 0x00000000ff250698, 0x00000000ff400000) from space 1024K, 62% used [0x00000000ff500000, 0x00000000ff59fd38, 0x00000000ff600000) to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00020, 0x00000000ffa00200, 0x0000000100000000) Metaspace used 3486K, capacity 4496K, committed 4864K, reserved 1056768K class space used 386K, capacity 388K, committed 512K, reserved 1048576K
这里有点问题感觉,不知道4MB的allocation4为什么被分配到了老年区。jdk版本是1.8的。
二、大对象直接进入老年代
public class Two { private static final int _1MB = 1024 * 1024; /* * VM参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=3145728 */ public static void testPretenureSizeThreshold(){ byte[] allocation; allocation = new byte[4 * _1MB]; } public static void main(String[] args) { testPretenureSizeThreshold(); } }
Heap def new generation total 9216K, used 2372K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 28% used [0x00000000fec00000, 0x00000000fee51270, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000) Metaspace used 3466K, capacity 4496K, committed 4864K, reserved 1056768K class space used 383K, capacity 388K, committed 512K, reserved 1048576K
三、长期存活的对象将进入老年区
/*-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution*/
public class Three { private static final int _1MB = 1024 * 1024; @SuppressWarnings("unused") public static void testTenuringThreshold() { byte[] allocation1, allocation2, allocation3; allocation1 = new byte[_1MB / 4]; //0.25M allocation2 = new byte[4 * _1MB]; allocation3 = new byte[4 * _1MB]; allocation3 = null; allocation3 = new byte[4 * _1MB]; } public static void main(String[] args) { testTenuringThreshold(); } }
[GC (Allocation Failure) [DefNew Desired survivor size 524288 bytes, new threshold 1 (max 1) - age 1: 943224 bytes, 943224 total : 6560K->921K(9216K), 0.0026489 secs] 6560K->5017K(19456K), 0.0026787 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew Desired survivor size 524288 bytes, new threshold 1 (max 1) : 5017K->0K(9216K), 0.0011371 secs] 9113K->5013K(19456K), 0.0011520 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 4178K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 5013K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 48% used [0x00000000ff600000, 0x00000000ffae5498, 0x00000000ffae5600, 0x0000000100000000) Metaspace used 3496K, capacity 4498K, committed 4864K, reserved 1056768K class space used 387K, capacity 390K, committed 512K, reserved 1048576K
这里的结果和书上不一致,我觉得可能是jdk版本的问题。
四、动态年龄判定
public class Four { /** -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+UseSerialGC -XX:+PrintTenuringDistribution **/ private static final int _1MB = 1024*1024; @SuppressWarnings("unused") public static void testTenuringThreshold2(){ byte[] allocation1, allocation2, allocation3, allocation4; allocation1 = new byte[_1MB / 4]; //allocation1 + allocation2 等于survivor空间的一般 allocation2 = new byte[_1MB / 4]; allocation3 = new byte[4 * _1MB]; allocation4 = new byte[4 * _1MB]; //eden区不够,发生第一次Minor GC allocation4 = null; //无GC Roots引用,下次Minor GC会回收掉4MB的内存 allocation4 = new byte[4 * _1MB]; } public static void main(String[] args) { testTenuringThreshold2(); } }
[GC (Allocation Failure) [DefNew Desired survivor size 524288 bytes, new threshold 1 (max 15) - age 1: 1048576 bytes, 1048576 total : 6816K->1024K(9216K), 0.0031233 secs] 6816K->5247K(19456K), 0.0031555 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew Desired survivor size 524288 bytes, new threshold 15 (max 15) - age 1: 1688 bytes, 1688 total : 5203K->1K(9216K), 0.0010271 secs] 9427K->5248K(19456K), 0.0010408 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 9216K, used 4235K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff0227f0, 0x00000000ff400000) from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400698, 0x00000000ff500000) to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) tenured generation total 10240K, used 5247K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) the space 10240K, 51% used [0x00000000ff600000, 0x00000000ffb1fcd0, 0x00000000ffb1fe00, 0x0000000100000000) Metaspace used 3492K, capacity 4498K, committed 4864K, reserved 1056768K class space used 387K, capacity 390K, committed 512K, reserved 1048576K
关于垃圾分代收集的一些思考
现有的垃圾收集器一般采用的分代收集的思想。将整个Java堆分为新生代Young和老年代Old,而新生代又分为Eden,Survivor From,Survivor To(比例为8:1:1)。
首先考虑第一个问题:为什么要有Survivor区?
新生代采用的是复制算法来回收垃圾,如果没有Survivor区,Eden区每进行一次Minor GC,存活的对象就会复制到老年代,这样老年代很快就被填满了,然后出发Major GC。而老年代的内存一般大于新生代,进行一次Major GC消耗的时间比Minor GC长得多,会严重影响程序的执行效率。
为什么要设置两个Survivor区?
设置两个Survivor区最大的好处就是解决了碎片化。
下面看一个例子:
新建的对象在Eden区中,一旦Eden区满了,触发一次Minor GC(Eden和Survivor From会被清空),Eden区中的存活对象就会被移动到Survivor区。而等到下一次Eden区满的时候,又要进行一次Minor GC,而Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化,如下图:
建立两块Survivor区,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块Survivor Space “From”,Eden被清空;等Eden区再满了,就再触发一次Minor GC(Eden和Survivor From会被清空),Eden和Survivor From中的存活对象又会被复制送入第二块Survivor To(这个过程非常重要,因为这种复制算法保证了To中来自From和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。From和Eden被清空,然后下一轮From与To交换角色,如此循环往复。如果对象的年龄达到15,该对象就会被送到老年代中。
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!