JVM新生代、老年代的默认比值真的是1:2吗?

原创
云计算 虚拟化 开发工具
如果你启动进程时未添加任何 JVM 调优参数,也就是说完全默认选项,那么无论你用的是哪个 JDK 版本,新生代、老年代的默认比值早就不是 1:2 了!

[[392981]]

 图片来自 Pexels

【51CTO.com原创稿件】业界的普遍认知如下图所示,正确吗?

结论先行:如果你启动进程时未添加任何 JVM 调优参数,也就是说完全默认选项,那么无论你用的是哪个 JDK 版本,新生代、老年代的默认比值早就不是 1:2 了!

Eden、Survivor From、Survivor To 的默认比值也早就不是 8:1:1 了!理由慢慢道来。

01先说 JDK 版本

从 JDK1.5 开始,公开版本号改为 JDK5 的命令方式,只有开发版本号才沿用 JDK1.5 的命令方式。

截止目前,最新的 JDK 版本是 2021 年 3 月份发布的 JDK16,本文阐述的范围将囊括所有的 JDK 版本,但是以 JDK8 为主线,因为 JDK8 目前的市场占有率仍然最高,并且 JDK8 属于 LTS 版本(Long Term Support,长期支持)。

JDK8 于 2014 年 3 月发布,JDK8 承诺的最后支持日期是:2030 年 12 月。

JDK8 之后的下一个 LTS 版本是于 2018 年 9 月发布的 JDK11,JDK11 承诺的最后支持日期是:2026 年 9 月,尚不如 JDK8 支持的时间久。

再下一个 LTS 版本是将于 2021 年 9 月发布的 JDK17。JDK 的版本发布,从 JDK9 开始改为严格基于时间的模式,固定每半年推出 1 个新版本,每 3 年推出 1 个 LTS 版本。

02再说 GC 回收器

说明如下:

①根据 JDK 最新版本,除上面提到的 9 个 GC 回收器外,还有 1 个是 JDK11 引入的 Epsilon 回收器。

也就是说历史至今,目前一共有 10 个 GC 回收器,Epsilon 回收器不执行任何垃圾回收的工作(A No-Op Garbage Collector)。

比如针对如性能测试等期望排除 GC 性能影响且进程执行生命周期较短的特别场景。所以不在本文的讨论范畴内。

②ZGC 和 Shenandoah 的设计目标类似,均为针对 G1 的不足。唯独 ZGC 由 Oracle 公司发起,Shenandoah 由 RedHat 公司发起,所以官方重视程度会有些许差异。

另外,直到 JDK16,这 2 个回收器仍然未取代 G1 成为默认选项。尽管官宣这 2 个回收器可兼顾吞吐量和响应时间,但每个 JDK 版本都在持续优化,尚未达到成熟。

③-XX:+UseParNewGC 的组合已在 JDK8 的“JEP 173: Retire Some Rarely-Used GC Combinations”废弃。详见:http://openjdk.java.net/jeps/173。

原因是:很少使用的组合。

④-XX:+UseConcMarkSweepGC 的组合中的 CMS 回收器已在 JDK9 的“JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector”不再建议使用。

并且已在 JDK14 的“JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector”废弃。

详见:http://openjdk.java.net/jeps/291、http://openjdk.java.net/jeps/363。

原因是:G1 已成为默认回收器,G1 同 CMS 一样是响应时间优先,G1 使命就是替换掉 CMS。

每个 GC 回收器具体的功能和原理,由于篇幅有限,本文就不做细节的阐述。

03新生代、老年代的默认比值由哪个参数决定

NewRatio,默认值:2,也就是说新生代和老年代的默认比值是 1:2。

如下图所示,不仅 JDK8 的 NewRatio 默认值是 2,最新版 JDK16 的 NewRatio 默认值仍然也是 2。SurvivorRatio,默认值:8,效果同。

尽管 NewRatio 和 SurvivorRatio 的默认值是没有问题的,但是未必实际生效,下面将阐述具体的验证分析过程。

04验证分析

以下验证分析过程均基于 JDK8。

①启动进程时不添加任何 JVM 参数

默认:-XX:+UseParallelGC,Parallel Scavenge+Parallel Old 的 GC 回收器组合。

如下图,的确 NewRatio=2,并且 NewSize=42.0MB,OldSize=84.0MB,符合 1:2。

但是看 Heap Usage 详情,Eden Space capacity=306.5MB,相比 PS Old Generation capacity=130.5MB 还要更多呢。

另外 Eden、Survivor From、Survivor To 的比值也明显不是 8:1:1,为什么会这样?

②改用其他 GC 回收器的参数试试?

-XX:+UseSerialGC:Serial+Serial Old 的 GC 回收器组合。

看 Heap Usage 详情,多了一块 New Generation=Eden+1 Survivor Space。

如果再加上另外 1 个 Survivor Space(From Space 或者 To Space),刚好是 42.0MB,同 OldSize 的比值是 1:2,没有任何问题。

另外,Eden、Survivor From、Survivor To 的比值也明显是 8:1:1。

-XX:+UseConcMarkSweepGC:ParNew+CMS 的 GC 回收器组合。

看 Heap Usage 详情,效果与 -XX:+UseSerialGC 基本一致,也没问题。

-XX:+UseG1GC:G1 回收器。

看 Heap Usage 详情,也不合符 NewRatio 标识的 1:2 的默认比值。另外 From Space 和 To Space 也消失了,为什么会这样?

其他组合:要么被 Deprecate,要么被 Remove,就不做参考了。

③原因分析

(1)在 JDK 1.3 及之前,-XX:+UseSerialGC 是回收器的唯一选择。当时来说,JVM 新生代、老年代的默认比值的的确确是 1:2。

(2)-XX:+UseConcMarkSweepGC 类似于 -XX:+UseSerialGC 的多线程版本,并且有代码框架的复用,所以表现一样。

(3)-XX:+UseSerialGC、-XX:+UseG1GC、-XX:+UseZGC、-XX:+UseShenandoahGC,都没有使用传统的 GC 代码框架,所以表现不一样。

(4) 基于上一点,值得注意的是 JDK7U4 之前 -XX:+UseParallelGC 的老年代 GC 是 Serial Old。

其实也并没有复用 Serial Old 的代码框架,-XX:+UseParallelGC 的老年代 GC 叫:PS MarkSweep,其实现原理与 Serial Old 非常接近,所以包括官方在内的许多资料统一用 Serial Old 来称呼而已。

(5) UseAdaptiveSizePolicy 参数,非常重要!该参数默认开启,直到最新版 JDK16 仍然开启。

该参数对 -XX:+UseSerialGC 和 -XX:+UseConcMarkSweepGC 无论开启与否,均不生效。

该参数对应的是 GC 自适应的调节策略(GC Ergonomics),如果开启,那么 JVM 会根据系统的运行情况,动态调整一些参数,包括:新生代和老年代的比值。

Eden、Survivor From、Survivor To 的比值;大对象直接进入老年代的阈值等,以达到吞吐量优先的目标。

④关闭 UseAdaptiveSizePolicy 参数试试?

开启参数是在 -XX 后面带加号,关闭参数是在 -XX 后面带减号。启动进程时候添加 JVM 参数 -XX:-UseAdaptiveSizePolicy。

GC 回收器仍然保持默认:-XX:+UseSerialGC,Parallel Scavenge+Parallel Old 的组合。

查看 Heap Usage 详情,一切正常了,新生代、老年代的比值是 1:2。Eden、Survivor From、Survivor To 的比值也是 8:1:1。

⑤UseAdaptiveSizePolicy 参数相关源码分析

从 http://hg.openjdk.java.net 获取 hotspot 的源码。

-XX:+UseParallelGC 的新生代实现在 psScavenge.cpp,老年代实现在 psOldGen.cpp,均在 hotspot\src\share\vm\gc_implementation\parallelScavenge 目录下。

如下图,当 UseAdaptiveSizePolicy 默认开启,那么进入动态调整的处理逻辑。

如下图,是进入动态调整的处理逻辑后的最核心代码 heap→resize_young_gen。

⑥最新的 -XX:+UseZGC 或 -XX:+UseShenandoahGC 呢?

G1 回收器也是遵循分代收集理论的,但是会把连续的 Java 堆不区分新生代、老年代的情况下而划分为大小相等的 Region,每个 Region 都会根据需要扮演新生代或老年代的空间。

G1 仍然保留了新生代、老年代,并且新生代也区分 Eden 和 Survivor,只是 Survivor 不再区分 From 和 To。

而 ZGC 和 ShenandoahGC 的话,颠覆的比较彻底,已经不再区分新生代和老年代了,也就是说不再使用分代收集,默认比值多少的问题已经没有意义了。

05结论

①NewRatio,默认值是 2,这个是亘古不变、千真万确的,哪怕最新的 JDK16 版本仍然也是。

毕竟最最历史悠久、居功至伟的 -XX:+UseSerialGC 始终既没有被 Deprecate,也没有被 Remove。目前依然是 Client 模式下的默认选项。

②JVM 新生代、老年代的默认比值跟选择的 GC 回收器有关!不仅仅只是有关,同时还决定着新生代、老年代的概念是否存在。

③从 -XX:+UseParallelGC 取代 -XX:+UseSerialGC 成为默选项起,JVM 新生代、老年代的默认比值早已不再是 1:2 了,而是比值可被动态调整,比如目前市场占有率最高的 JDK8。

④从 JDK9 开始,-XX:+UseG1GC 取代 -XX:+UseParallelGC 成为默认选项。

或者是今后 -XX:+UseZGC 或 -XX:+UseShenandoahGC 可能成为默认选项(毕竟设计目标就是针对 G1 的不足)。

JVM 新生代、老年代的默认比值更不可能再是 1:2 了,就连新生代、老年代的概念都可能不存在了。

所以,JVM 新生代、老年代的默认比值是 1:2 的错误认知,持续已有 10 年之久。

面试官和求职者在未经考究的情况下总是“默契有佳”!期望通过本文可以改变行业的一些错误认知。

另外,纵使被业界奉为:JVM 葵花宝典宝典 & 资深 JVM 教科书级别的《深入理解 Java 虚拟机》,书中不少论点也并未及时得到更新,可能会对读者产生一些的误导。

尽信书不如无书,拿死记硬背的面试题去糊弄求职者的面试官就是耍流氓。

作者:大黄蜂

简介:曾就职于华为、腾讯等大型互联网公司,于 2018 年 5 月加盟独角兽公司 akulaku 担任技术管理职务,对分期、金融借贷等核心系统的架构设计具有丰富的实战经验。精通 Redis 和 JVM,非常重视底层原理,对高级用法、协议、源码等具有深入的研究。并且,具有自己独特的团队管理理念,另辟蹊径,专注研发质量和效率,为公司培养出多名青年高潜,并多次荣获各类表彰。

编辑:陶家龙

征稿:有投稿、寻求报道意向技术人请添加小编微信 gordonlonglong

【51CTO原创稿件,合作站点转载请注明原文作者和出处为51CTO.com】

责任编辑:武晓燕 来源: 51CTO技术栈
相关推荐

2021-08-19 15:27:47

新生代农民工软件信息技术

2024-10-09 09:07:10

JVM优化String类JDK1.6

2022-04-08 07:51:31

JavaJVM垃圾回收

2009-04-13 09:37:42

IT新生代创业

2009-07-01 16:48:43

JAVA程序员

2011-09-14 09:31:45

2021-08-18 07:37:02

程序员农民工人力

2022-04-29 08:00:51

V8垃圾回收

2024-02-05 19:06:04

DartVMGC流程

2021-08-19 20:57:21

设计模式策略

2011-06-28 09:13:33

OpenFlow云计算

2009-06-10 13:27:36

3G

2021-05-11 21:56:11

算法清除JVM

2013-04-24 10:10:26

2015-07-23 14:57:36

大数据供应商

2023-06-19 14:55:48

2021-05-25 11:27:07

ZTouch
点赞
收藏

51CTO技术栈公众号