共计 2161 个字符,预计需要花费 6 分钟才能阅读完成。
背景
在公司外部技术群里,常常有人时不时的问到服务某次 GC 工夫忽然很高,有什么方法排查。基本上每次都会有人狐疑会不会 Swap 导致的,先看看 Swap,如果真的应用了 Swap 区域,基本上就会让 Swap 区域背锅了。
案例
案例一:CMS GC 工夫飙升
有次群里有人给出一个 case:CMS GC 工夫飙升,次要是 remark 阶段的解决工夫太长,给出的日志如下:
群里有人应用到了 Swap 区域。而对 CMS GC 如果有理解的话,想问的必定是是不是加了“-XX:+CMSScavengeBeforeRemark
”这个参数。在明确告知有这个参数。然而我在这个 remark 阶段并没有看到 young gc,提问者就又截了一个全一点的日志,如下:
感觉日志在 remark 之前并没有进行 young gc,失常的退出“-XX:+CMSScavengeBeforeRemark
”参数日志应该如下:
于是又让提问者确认一下,在 remark 工夫失常的状况下是不是进行了 young gc,在 remark 工夫异样的状况下没有进行 young gc,失去的答案是。那么问题基本上就转化成了“为什么在 remark 之前没有进行 young gc 呢”
而后带着这个问题去 Google 基本上就比拟容易找到答案了,根本能够概述为在执行 JNI 时候,有可能会导致 JVM 阻止执行 young gc。能够参考:
答案一
答案二
退出参数:+PrintJNIGCStalls 能够验证该问题。
案例二:Young GC 工夫飙升很高
这个笔者经验的一个 case,监控 & 日志如下:
因为 gc log 中只能看到 GC 总工夫看不到哪个阶段呈现问题,所以把垃圾回收齐切换到 G1,看到的 log 如下:
发现 Termination 比拟久,Object copy diff 太大导致的,也就是 CPU 忙碌水平不统一导致的,批改 gc 线程数小于 cpu 问题解决。
如何判断是不是 Swap 区域导致 GC 异样
如果 JVM 堆内内存大于等于零碎内存的话,Java 过程呈现了大量应用 Swap 区域对 GC 影响的确比拟大。如果产生 GC 抖动时,零碎没有应用 Swap 区域或者 Java 过程没有应用 Swap 区域,就能排除 Swap 起因。
因为我司只对系统应用 Swap 区域的整体状况做了监控,并未对 Java 过程应用 Swap 区域做监控,GC 抖动基本上是小概率事件,所以很难从监控做出判断的。
那么其余状况,如何大抵判断进去是不是 Swap 导致的 GC 异样呢?
首先,咱们得理解 Swap 相干的基本知识。
Swap 区域次要解决外部有余的问题,把局部硬盘当做虚拟内存应用。
Swap 中最要害的零碎参数:vm.swapiness(0-100),该参数值越小示意当内存不足时,偏向于通过回收 cache 区域,而不是把过程内存替换到 Swap 区域。所以该值应该设置小一点就能缩小 Swap 可能对 GC 产生的影响,比方我司对立默认设置为 1。
Swap 内存回收算法应用的是 LRU 算法,他会标记处沉闷页面和非沉闷页面,也就是说如果内存始终被应用基本上常驻内存,不会被替换到 Swap。
GC 的哪些阶段可能受 Swap 影响
young gc
young gc 的特色是较为频繁,基本上每分钟都会屡次。young gc 次要有两个阶段,一个是扫描阶段、一个是对象复制阶段。扫描阶段会从根汇合扫描标记 Eden、From 中的存活对象,而后对象复制阶段把存活对象 copy 到 To 区域中去。
复制阶段:因为 young gc 较为频繁就会导致 Eden、From、To 区域不太可能被置换到 Swap 区域,所以复制阶段不太可能受到 Swap 区域影响;假如 young gc 不频繁,那么在刚刚经验了扫描阶段,Eden、From 也必定会在内存中,只有 To 区域有可能会受到 Swap 影响。
相比拟于复制阶段,扫描阶段就绝对简单一点。这次要跟根汇合有关系,young gc 的根汇合次要有线程上下文、old 区域、Class、JNI 援用等,像 JNI 援用、Class 等长时间不应用有可能被 OS 置换到 Swap。所以该阶段有可能因为 Swap 影响 GC。
cms gc
cms gc 次要分为:初始标记、并发标记、并发预清理、从新标记、并发清理等阶段,只有初始标记和从新标记会 stop the world,所以咱们只须要关注这两个阶段即可。
初始标记:该阶段标记 GC Roots 能间接关联到的对象。所以该阶段和 young gc 的扫描阶段相似,也有可能因为 Swap 影响到 GC。
从新标记:因为在并发标记和并发预清理这个阶段,用户线程和 GC 线程并发,如果这个阶段用户线程产生了新的对象,总不能被 GC 掉吧。这个阶段就是为了让这些对象从新标记。在这个阶段拜访到的内存肯定是之前刚刚拜访过的,所以这个阶段不太可能由 Swap 区域导致 GC 异样。
总结
对于 CMS GC,如果在 remark 阶段异样行为而 InitialMark 是失常的,基本上能够排除 Swap 导致的 GC,young gc 在 copy 阶段异样而 Root Scaning 失常也基本上能够排除 Swap 因素。
我想大家喜爱让 Swap 背锅的起因有两个:
- 对 Swap 如何影响 GC 以及可能影响到 GC 哪些阶段不太理解;
- GC 工夫异常情况下,的确较难剖析和排除;必须要对 GC 的具体过程,GC 工具等有较为深刻的理解。
参考:《2020 最新 Java 根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/691651…