java垃圾回收诡异现象

在知乎上看到一篇提问,于是做了个实验帮助他解答,这里整理成一篇文章分享一下。

先看代码如下代码:

 1 /**
 2  * Created on 2017/12/16.
 3  *
 4  * -verbose:gc -XX:+UseSerialGC -Xms6M -Xmx6M -Xmn2M -XX:+PrintGCDetails
 5  */
 6 public class TestHeap {
 7     private static final int _1MB = 1024*1024;
 8 
 9     public static void main(String[] args) throws Exception{
10         byte[] b = new byte[3*_1MB];
11     }
12 }

运行时加上注释里的JVM参数,控制台打印如下

问题是:分配一个3m的数组,新生代只有2m,所以对象直接分配到年老代。通过打印结果也可以看出来,年老代用了3072k,正好是我们的3m数组。但是为什么新生代居然还有68%不占用呢?

简单猜想一下,肯定是Java在运行时自己创建了一些对象占用了新生代空间。好我们来验证下。

将代码为什么也不做:

/**
 * Created on 2017/12/16.
 *
 * -verbose:gc -XX:+UseSerialGC -Xms6M -Xmx6M -Xmn2M -XX:+PrintGCDetails
 */
public class TestHeap {
    private static final int _1MB = 1024*1024;

    public static void main(String[] args) throws Exception{
        //byte[] b = new byte[3*_1MB];
    }
}

控制台打印如下:

即使执行创建字节数组语句,新生代依旧被占用68%。可以说我们假象成立。

不信?

那好吧,咱们就打印下此时Java堆空间里的对象来看看。

 咱们是用jmap命令来打印出堆里的对象看看,顺便复习下jmap的使用。

我们使用如下命令:

jmap -histo:live pid 

注意:此命令不能在线上执行,因为会触发JVM的fullgc。

代码不变,依旧是注释掉字节数组的创建语句。只不过为了方便执行jmap命令,咱们让它暂停下,以防进程退出。 

/**
 * Created on 2017/12/16.
 *
 * -verbose:gc -XX:+UseSerialGC -Xms6M -Xmx6M -Xmn2M -XX:+PrintGCDetails
 */
public class TestHeap {
    private static final int _1MB = 1024*1024;

    public static void main(String[] args)throws Exception{
        //byte[] b = new byte[3*_1MB];
        Thread.sleep(100000L);
    }
}

执行jmap命令结果如下(部分截图):

 

看到了,我们啥都不做都有1544个char数组对象,120个byte数组对象等等。

---------------------------------------------------------------------------------------------------------------------

这里是跟阿里的技术大牛创建的一个圈子,主要面向初学者提供辅导帮助。有兴趣的可以加入。

https://t.xiaomiquan.com/aEQVNJe

原文地址:https://www.cnblogs.com/qingquanzi/p/8046042.html