关于64位JVM默认启动模式与默认GC那点事

122 阅读3分钟

这篇文字的由来是笔者在阅读周志明《深入理解Java虚拟机》时,在内存分配与回收策略中实践操作和书本中结果不同而产生的。

原问题

在《深入理解Java虚拟机》3.8节内存分配与回收策略 中,作者通过调用-XX:+PrintGCDetails 以验证对象在堆中的分配原则,如案例验证大对象直接进入老年代中,构建了如下实例:

/**
 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * -XX:PretenureSizeThreshold=3145728
 */
public class TestPretenureSizeThreshold {
    public static final int _1MB=1024*1024;
    public static void main(String[] args) {
        byte[] allocation1;
        allocation1=new byte[4*_1MB];
    }
}

如果按照书本所言,运行结果应该为:

IMG_20220705_152019_edit_335586727498272.jpg 然而在笔者的电脑上,使用IDEA运行时,运行结果为:

image.png

对比二者运行结果,很显然有结果: 原文中的大对象直接进入老年代,而后者依旧进入新生代

并且,对于书上的其他案例,同样出现了与作者运行结果不符情况。

解决流程

起初,笔者以为是自己参数抄录有误或是验证代码逻辑与书上不同,因此进行了多次检查,但是实际证明并没有错误(其实除非是数字填写错误,参数名错误运行时会立刻报错的)。

而周志明先生的书作为Java畅销多年的名作,通常不会犯结果展示错误的低级错误,那么是哪里出了问题呢?


省略两小时~


在排查了诸多因素后,笔者注意到周志明老师在本节初提到本节使用的是Serial加Serial Old客户端默认收集器,由于默认二字,笔者并没有对收集器的选择作过多关注,然而实际上,可以通过java自带的jconsole工具看到,在笔者电脑上,可以看到IDEA使用的收集器为:

image.png

(根据网络资料其中PS MarkSweep相当于Serial Old,两者共用代码)

其实根据一开始的运行结果,也可以看出使用的并不是Serial GC,但是由于笔者经验不足没有发现

GC.png

只要通过添加 -XX:+UseSerialGC参数即可将收集器转化为Serial加Serial Old的组合,运行结果如下:

image.png

为何默认使用的不是Serial GC?

在解决完上面问题后,笔者依然存在着疑惑,明明周志明先生在书上说java客户端模式默认使用的是Serial GC,为何在笔者电脑上不是这样呢?

参考happydecai的博客,笔者查阅了自己电脑上的默认运行模式:

image.png

可以看到,使用的是Server模式而不是client模式。

然而根据happydecai在博客中所言,32位系统上可以使用client模式与Server模式,64位系统上只支持Server模式。

以此笔者推测,可能周志明先生在编写《深入理解java虚拟机》时使用的是32位系统,因此不必指定使用-XX:+UseSerialGC的参数,直接运行原文的代码即可。


后来在5.3.1节中验证了周志明先生使用的是32位系统这点

IMG_20220705_183814_edit_339103330143568.jpg