docker 中 jvm 参数设置的坑
max heap 值的设置问题
比如,启动java 程序的时候设置 max heap =32g,发现线程程序模拟奇妙的被杀死。
cause: docker 设置应用内存为30g。
比如jvm 的内存在30g的时候,以为还有内存,不会做gc,然后就被docker 杀死了。
这个就是为什么,过一段时间,我们的程序就被kill,但是也不做gc 释放内存。
youngc 的时间特别长
我们的 eden 大概10个g,比较大,但是 youungc 打印log看
2020-08-20T10:50:00.180+0800: 166787.463: [GC (Allocation Failure) [PSYoungGen: 4735255K->126962K(4789760K)]
6090671K->1552564K(11939840K), 18.6624568 secs]
[Times: user=33.16 sys=4.14, real=18.67 secs]
这个是无法接受的。
我们的jdk8,默认的是 UseParallelGC, young 区用的是 paraNew的 并发收集算法
虽然 UseParallelGC,关注吞吐,可能单次回收时间慢一点,1s 我觉得是正常的,尼玛一看30s,不可能的。
jstack 看,分配了23个gc线程。
年轻代的并行收集线程数默认是(ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8),如果你希望设定这个线程数,可以通过-XX:ParallelGCThreads= N 来调整。
可以看到,docker 拿的是宿主机的 cpu数目,但是docker 容器有一个 核的限制。
我们一看资源,大概就是 2个cpu。
结果24个cpu去收集垃圾,但是只有2个核心,造成了大量的竞争,所以非常慢。
指定成2个cpu,gc 时间回到 1s级别。
后续看下来,还是有2s左右的停顿:
垃圾回收算法的选择
通过gc log 看,时间在 youngc 的时候不太稳定, 有时候能到1-2s的停顿
默认使用的 parallel scaverage, 吞吐量优先。 有可能存在一个 大的停顿。
parallelNew,就是普通的并发,没有吞吐量优先,可能效果好稍微好点。
影响 停顿的几个变量, young 区的大小, gc 线程的数目, 以及 选用的算法。
目前看使用 g1, 回收次数变多,但是平均时间相对是最小的。
考虑到线上4个线程:
我们在167 上试用 3个类型的 gc 算法:
1) parallel scaveage:
4 次yonguc,1.3s,平均 300ms,3次full gc。meta比较快,最大0.52s
2) parallel new:
4次 youngc,0.7s。平均 170ms。 3次 full gc。meta 区,做大0.3s。
3) g1:
18次,2.3s。平均 120ms,最大 0.35s。
看起来G1 比较优秀。
如果是 java11,可以使用更优秀的;zgc,低延迟可以做到更低。
参考美团的大作:
https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html