JVM测试总结_基于Java8(自用)

一、jvm垃圾回收器

引用链接:https://www.cnblogs.com/chenpt/p/9803298.html

jvm的垃圾回收器有serial收集器、parnew收集器、parallel scavenge收集器、serial old 收集器、parallel old收集器、cms收集器、g1收集器

HotSpot虚拟机所包含的收集器:

图中展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。

新生代收集器:Serial、ParNew、Parallel Scavenge

老年代收集器:CMS、Serial Old、Parallel Old

整堆收集器: G1

二、jvm内存分配担保机制

引用链接:https://mp.weixin.qq.com/s?__biz=MzA5MzQ2NTY0OA==&mid=2650797224&idx=1&sn=5819bd097e6dabab34db382be2d0182c&chksm=885629f7bf21a0e1fe382626f8d272ee9c4066109f329ef93a205c29e37e9376f455c1710903&scene=21#wechat_redirect

1、指定Serial+Serial Old作为jvm垃圾回收器

jvm启动参数如下:

-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC

其中10M分配给新生代,另外10M分配给老生代。新生代中eden和survivor区的比例为8:1,eden区所分配内存为8M,两个survivor区分别为1M,新生代总可用空间为9M。

测试代码如下:

package handlepromotion;

public class HandlePromotionFailure {
    private static final int _1MB = 1024 * 1024;
    
    public static void testHandlePromotionFailure() {
        byte[] allocation1 = new byte[2* _1MB];
        byte[] allocation2 = new byte[2* _1MB];
        byte[] allocation3 = new byte[2* _1MB];
        byte[] allocation4 = new byte[4* _1MB];
    }
    
    public static void main(String[] args) {
        testHandlePromotionFailure();
    }
    
}

测试结果:

[GC (Allocation Failure) [DefNew: 7127K->526K(9216K), 0.0062127 secs] 7127K->6670K(19456K), 0.0062962 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
def new generation total 9216K, used 4704K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
from space 1024K, 51% used [0x00000000ff500000, 0x00000000ff583918, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
Metaspace used 2671K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K

测试分析:

eden区依次创建了三个2M的对象,占用内存6M,新生代总可用空间为9M,eden区可用空间为8M,由于9M-6M=3M<4M,从而进行GC,发现survivor区空间不足。此时,JVM就启动了内存分配的担保机制,把这6MB的三个对象直接转移到了老年代。此时就把新生代的空间腾出来了,然后把第四个对象(4MB)放入了eden区中。

2、指定Parallel Scavenge+Serial Old作为jvm垃圾回收器

jvm参数将-XX:+UseSerialGC修改为-XX:+UseParallelGC

测试代码如下:

package handlepromotion;

public class HandlePromotionFailure {
    private static final int _1MB = 1024 * 1024;
    
    public static void testHandlePromotionFailure() {
        byte[] allocation1 = new byte[2* _1MB];
        byte[] allocation2 = new byte[2* _1MB];
        byte[] allocation3 = new byte[2* _1MB];
        byte[] allocation4 = new byte[4* _1MB];
    }
    
    public static void main(String[] args) {
        testHandlePromotionFailure();
    }
    
}

测试结果:

Heap
PSYoungGen total 9216K, used 7292K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 89% used [0x00000000ff600000,0x00000000ffd1f050,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
Metaspace used 2670K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K

接下来我们将第四个对象由4M改为3M,测试结果如下:

[GC (Allocation Failure) [PSYoungGen: 7127K->632K(9216K)] 7127K->6784K(19456K), 0.0053005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 632K->0K(9216K)] [ParOldGen: 6152K->6669K(10240K)] 6784K->6669K(19456K), [Metaspace: 2664K->2664K(1056768K)], 0.0082392 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 9216K, used 3154K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 38% used [0x00000000ff600000,0x00000000ff914930,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 6669K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 65% used [0x00000000fec00000,0x00000000ff283538,0x00000000ff600000)
 Metaspace       used 2671K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

测试分析:

通过和上面的测试结果对比,我们发现当第四个对象无法放入eden区时,此时不会直接进行GC,而是判断如果要分配的内存>=eden区的一半,那么会直接把要分配的内存放入老年代中。否则进行GC操作,如果survivor区空间不足,则把eden区中的6M的对象直接放入老年代区中,eden区新创建4M的对象。

那么为什么3M的时候出现了FullGC呢,原作者也给出了他的答案(https://cloud.tencent.com/developer/article/1082687)。

大概的意思是这种情况下偶尔会发生FullGC,至于原因大概是在使用Parallel Scavenge收集器时,jvm会进行预测,如果晋升到老年代的平均大小大于老年代剩余大小,则认为需要一次FullGC。当虚拟机估算出下次分配可能会发生无法分配的问题时,则会提前发生一次FullGC。

那么针对上面的例子,当gc操作把eden区的6M全部晋升到老年代区中,此时老年代区的大小为10M-6M=4M,jvm预测下一次晋升时老年代空间可能不足,于是提前进行了一次FullGC。

简单验证下,修改jvm参数为-Xms24M -Xmx24M -Xmn10M,将heap内存加大,新生代不变,此时老年代大小为14M。

此时测试结果如下,未发生FullGC:

[GC (Allocation Failure) [PSYoungGen: 7127K->632K(9216K)] 7127K->6784K(23552K), 0.0043651 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 3786K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 38% used [0x00000000ff600000,0x00000000ff914930,0x00000000ffe00000)
  from space 1024K, 61% used [0x00000000ffe00000,0x00000000ffe9e030,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 14336K, used 6152K [0x00000000fe800000, 0x00000000ff600000, 0x00000000ff600000)
  object space 14336K, 42% used [0x00000000fe800000,0x00000000fee02030,0x00000000ff600000)
 Metaspace       used 2671K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K
原文地址:https://www.cnblogs.com/java-meng/p/11096985.html