JVM实战:内存分配与回收策略

编程入门 行业动态 更新时间:2024-10-25 04:22:15

JVM<a href=https://www.elefans.com/category/jswz/34/1769775.html style=实战:内存分配与回收策略"/>

JVM实战:内存分配与回收策略

在阅读《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) (华章原创精品) - 周志明》一书中,为增强理解,跟着实践有感。

书中第3.8.1节:对象优先在Eden分配,实验结果如下:

然后我得到的不一样的结果(开始质疑书本的正确性),后来想到可能是因为JDK版本不同,使用的垃圾收集器不同。所以后面特意指定了垃圾收集器-XX:+UseSerialGC就与书上结果一致了。
为避免其他因素干扰,我在测试前使用了System.gz()强制JVM执行一次full gc。

private static final int _1MB = 1024 *1024;/*** VM参数:-XX:+UseSerialGC -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8* 注意不同的收集器内存分配和回收策略不一样,本例使用-XX:+UseSerialGC,虚拟机运行在Client模式下的默认值,Serial+Serial Old。得到的结果和书上一致*/public static void testAllocation() {System.gc();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}

下面展示每一步的内存分配和回收情况,可看到初始直接执行full gc后也并不能完全回收年轻代和老年代,会有一小部分空间被占用。然后allocation1 在eden区成功分配了2MB,allocation2,allocation3也成功分配,至此eden区已有6MB。

根据我们给JVM设置的参数,堆内存20MB,不可扩展(初始内存和最大内存都为20MB),年轻代10MB(可得老年代也为10MB),eden区和survivor区的比例为8(即8:1:1,survivor分from和to),所以eden区有8MB,survivor区1M,年轻代可用空间为9MB(年轻代采用的标记-复制算法,eden+survivor中的存活对象复制到另一个survivor)。

年轻代还剩不到2MB,不足以分配allocation4,因此触发了GC(原因是内存分配失败),而目前的6MB对象都还存活,无法回收,只好通过分配担保机制提前转移到老年代,所以老年代变成了6MB,然后年轻代分配了allocation4,变为4MB。

/**System.gc();
//        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
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0024904 secs] 2162K->724K(19456K), [Metaspace: 3426K->3426K(1056768K)], 0.0025292 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 246K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,   3% used [0x00000000fec00000, 0x00000000fec3d890, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 724K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   7% used [0x00000000ff600000, 0x00000000ff6b5240, 0x00000000ff6b5400, 0x0000000100000000)Metaspace       used 3434K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 374K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[Full GC (System.gc()) [Tenured: 0K->713K(10240K), 0.0021630 secs] 1994K->713K(19456K), [Metaspace: 3295K->3295K(1056768K)], 0.0022069 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 2458K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  30% used [0x00000000fec00000, 0x00000000fee66858, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 713K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b2600, 0x00000000ff6b2600, 0x0000000100000000)Metaspace       used 3318K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 362K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0018954 secs] 1994K->710K(19456K), [Metaspace: 3221K->3221K(1056768K)], 0.0019325 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 4506K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  55% used [0x00000000fec00000, 0x00000000ff066808, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b1920, 0x00000000ff6b1a00, 0x0000000100000000)Metaspace       used 3363K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 369K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[Full GC (System.gc()) [Tenured: 0K->710K(10240K), 0.0020691 secs] 1994K->710K(19456K), [Metaspace: 3192K->3192K(1056768K)], 0.0021076 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 6554K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  80% used [0x00000000fec00000, 0x00000000ff266818, 0x00000000ff400000)from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)tenured generation   total 10240K, used 710K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,   6% used [0x00000000ff600000, 0x00000000ff6b1828, 0x00000000ff6b1a00, 0x0000000100000000)Metaspace       used 3205K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 351K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[Full GC (System.gc()) [Tenured: 0K->724K(10240K), 0.0027738 secs] 2162K->724K(19456K), [Metaspace: 3421K->3421K(1056768K)], 0.0028359 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 6471K->25K(9216K), 0.0022628 secs] 7196K->6893K(19456K), 0.0022878 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heapdef new generation   total 9216K, used 4203K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)from space 1024K,   2% used [0x00000000ff500000, 0x00000000ff5064f8, 0x00000000ff600000)to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation   total 10240K, used 6868K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K,  67% used [0x00000000ff600000, 0x00000000ffcb5138, 0x00000000ffcb5200, 0x0000000100000000)Metaspace       used 3447K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 376K, capacity 388K, committed 512K, reserved 1048576K

下面再来验证下JDK8默认的垃圾收集器,首先CMD命令查看使用的是哪种收集器

java -XX:+PrintCommandLineFlags -version-XX:InitialHeapSize=267464320 -XX:MaxHeapSize=4279429120 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

UseParallelGC 即 Parallel Scavenge + Parallel Old,再查看详细信息

java -XX:+PrintGCDetails -versionjava version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
HeapPSYoungGen      total 76288K, used 2621K [0x000000076af80000, 0x0000000770480000, 0x00000007c0000000)eden space 65536K, 4% used [0x000000076af80000,0x000000076b20f748,0x000000076ef80000)from space 10752K, 0% used [0x000000076fa00000,0x000000076fa00000,0x0000000770480000)to   space 10752K, 0% used [0x000000076ef80000,0x000000076ef80000,0x000000076fa00000)ParOldGen       total 175104K, used 0K [0x00000006c0e00000, 0x00000006cb900000, 0x000000076af80000)object space 175104K, 0% used [0x00000006c0e00000,0x00000006c0e00000,0x00000006cb900000)Metaspace       used 2351K, capacity 4480K, committed 4480K, reserved 1056768Kclass space    used 254K, capacity 384K, committed 384K, reserved 1048576K

我们将对应参数改为-XX:+UseParallelGC,再来看一下结果:
allocation1, allocation2,allocation3可直接分配,与上文一致,不同的是allocation4,直接被分到了老年代(用的应该是大对象直接进入老年代的分配规则),由此可知不同收集器的实现方式是有差异的。

/**System.gc();
//        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
*/
[GC (System.gc()) [PSYoungGen: 1994K->776K(9216K)] 1994K->784K(19456K), 0.0006403 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 776K->0K(9216K)] [ParOldGen: 8K->705K(10240K)] 784K->705K(19456K), [Metaspace: 3174K->3174K(1056768K)], 0.0031807 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
HeapPSYoungGen      total 9216K, used 246K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 3% used [0x00000000ff600000,0x00000000ff63d890,0x00000000ffe00000)from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)ParOldGen       total 10240K, used 705K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb0560,0x00000000ff600000)Metaspace       used 3185K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 350K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[GC (System.gc()) [PSYoungGen: 2162K->808K(9216K)] 2162K->816K(19456K), 0.0015479 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 808K->0K(9216K)] [ParOldGen: 8K->716K(10240K)] 816K->716K(19456K), [Metaspace: 3339K->3339K(1056768K)], 0.0030067 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
HeapPSYoungGen      total 9216K, used 2457K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 30% used [0x00000000ff600000,0x00000000ff8667f8,0x00000000ffe00000)from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)ParOldGen       total 10240K, used 716K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 7% used [0x00000000fec00000,0x00000000fecb33a8,0x00000000ff600000)Metaspace       used 3362K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 369K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0007088 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 848K->711K(19456K), [Metaspace: 3257K->3257K(1056768K)], 0.0030776 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
HeapPSYoungGen      total 9216K, used 4506K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 55% used [0x00000000ff600000,0x00000000ffa66808,0x00000000ffe00000)from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)ParOldGen       total 10240K, used 711K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ce0,0x00000000ff600000)Metaspace       used 3337K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 363K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[GC (System.gc()) [PSYoungGen: 1994K->840K(9216K)] 1994K->848K(19456K), 0.0006964 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 840K->0K(9216K)] [ParOldGen: 8K->710K(10240K)] 848K->710K(19456K), [Metaspace: 3245K->3245K(1056768K)], 0.0028236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
HeapPSYoungGen      total 9216K, used 6554K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc66878,0x00000000ffe00000)from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)ParOldGen       total 10240K, used 710K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 6% used [0x00000000fec00000,0x00000000fecb1ac8,0x00000000ff600000)Metaspace       used 3302K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K/**System.gc();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
*/
[GC (System.gc()) [PSYoungGen: 1994K->792K(9216K)] 1994K->800K(19456K), 0.0013828 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 792K->0K(9216K)] [ParOldGen: 8K->711K(10240K)] 800K->711K(19456K), [Metaspace: 3259K->3259K(1056768K)], 0.0031560 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
HeapPSYoungGen      total 9216K, used 6570K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)eden space 8192K, 80% used [0x00000000ff600000,0x00000000ffc6a828,0x00000000ffe00000)from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)ParOldGen       total 10240K, used 4807K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)object space 10240K, 46% used [0x00000000fec00000,0x00000000ff0b1d60,0x00000000ff600000)Metaspace       used 3447K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 376K, capacity 388K, committed 512K, reserved 1048576K

补充:

设置垃圾收集器参数
-XX:+UseSerialGC,虚拟机运行在Client模式下的默认值,Serial+Serial Old。
-XX:+UseParNewGC,ParNew+Serial Old,在JDK1.8被废弃,在JDK1.7还可以使用。
-XX:+UseConcMarkSweepGC,ParNew+CMS+Serial Old。
-XX:+UseParallelGC,虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep)。
-XX:+UseParallelOldGC,Parallel Scavenge+Parallel Old。
-XX:+UseG1GC,G1+G1。

更多推荐

JVM实战:内存分配与回收策略

本文发布于:2024-03-10 02:56:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1726834.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:实战   分配   内存   策略   JVM

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!