性能文章>CMS GC 新生代默认是多大?>

CMS GC 新生代默认是多大?原创

2年前
7345312

问题

首先抛个问题给大家,看下面 JVM 参数配置:

-Xmx2g -Xms2g -XX:+UseConcMarkSweepGC

猜一猜按照这样的 JVM 参数配置,YoungGen(新生代)是多大呢?
你一定会觉得这还不简单吗,NewRatio 默认为 2,也就是 YoungGen 与 OldGen(老年代)的比例是 1:2,那 YoungGen 大小应该是 2048M/3 = 672M。
真的是这样吗?jmap -heap pid 看看

image.png

然而结果居然是 332.75M(说明下案例中的 JDK 版本是 7)。

分析

要想知道原因,只能撸源码了。
我们从 Arguments(是用来解析 JVM 参数)类的 setcmsandparnewgc_flags 函数说起,看函数名也知道是对 CMS 和 ParNew GC 的参数设置。

image.png

看提示 1,在 MaxNewSize 和 NewRatio 都是默认配置时,MaxNewSize 值为 preferredmaxnewsize,而 preferredmaxnewsize 是什么呢?
看提示 2,alignsizeup 主要是字节对齐用的,可以不用关系细节,所以 preferredmaxnewsize 主要取决于 preferredmaxnewsizeunaligned,
再看提示 3,preferredmaxnewsize_unaligned 的值为

MIN2(max_heap/(NewRatio+1), ScaleForWordSize(young_gen_per_worker * parallel_gc_threads))

也就是取 maxheap/(NewRatio+1) 和 ScaleForWordSize(younggenperworker * parallelgcthreads) 中较小的那个,max_heap/(NewRatio+1) 这个我们都了解,就是按照 NewRatio 来计算,ScaleForWordSize 又是什么呢?我们来看下 ScaleForWordSize 的定义:

image.png

因为这里我们是 64 位的机器,所以看上面的那行,alignsizedown_ 这个也是字节对齐的,所以 ScaleForWordSize 返回值约为 (x) * 13 / 10,也就是 younggenperworker * parallelgcthreads * 13 / 10 。因此,我们再看看 younggenperworker 和 parallelgcthreads 的取值,而 younggenperworker = CMSYoungGenPerWorker,
CMSYoungGenPerWorker 在另一块代码中有定义,跟硬件相关,x86 机器为 64M;而 parallelgcthreads 的值呢?
parallelgc_threads = (ParallelGCThreads == 0 ? 1 : ParallelGCThreads),所以我们得看 ParallelGCThreads 的设置

image.png

可知,ParallelGCThreads 在没有设置的情况下会设置成 parallelworkerthreads 函数返回值,我们接着看 parallelworkerthreads 函数:

image.png

再看 calcparallelworker_threads 函数:

image.png

在看 nofparallelworker_threads 函数:

image.png

根据上面三个函数,ParallelGCThreads 最终由 nofparallelworker_threads 函数计算出,其中 ncpus 是 cpu 的核数,测试机器是 4 核,所以 ncpus 为 4,按照上面的公式计算,因此, ParallelGCThreads 为 4。

所以绕了半天,ScaleForWordSize 的值大约是 64M * 4 * 13 / 10 = 332.8M,再做下对齐就得到 332.75M 了;max_heap / (NewRatio+1) 的值为2048M / 3 = 672M,而新生代的值取了较小的 ScaleForWordSize,故为 332.75M。

总结

看到上面的过程,是不是有点奔溃。YoungGen 的大小在没有设置的情况下是通过计算得出的,其大小可能与 NewRatio 的默认配置没什么关系而与ParallelGCThreads 的配置有一定的关系。
那么既然 YoungGen 大小有不确定性,我们最好还是通过这些 -XX:NewSize、-XX:MaxNewSize 或者 -xmn 参数设置下,免得遇到一些奇怪的 GC,让你措手不及。

点赞收藏
分类:标签:
涤生

目前就职于某大型互联网公司基础架构部,主要负责微服务框架、服务治理、Serverless 等中间件研发相关工作。欢迎关注“涤生的博客”公共号。

请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

记一次线上RPC超时故障排查及后续GC调优思路

记一次线上RPC超时故障排查及后续GC调优思路

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

【全网首发】从源码角度分析一次诡异的类被加载问题

【全网首发】从源码角度分析一次诡异的类被加载问题

12
3