9

为什么GC 异常,大家喜欢让Swap背锅呢

 3 years ago
source link: https://club.perfma.com/article/2081246
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
为什么GC 异常,大家喜欢让Swap背锅呢
文章>为什么GC 异常,大家喜欢让Swap背锅呢

为什么GC 异常,大家喜欢让Swap背锅呢

朱纪兵1天前

在公司内部技术群里,经常有人时不时的问到服务某次GC时间突然很高,有什么办法排查。基本上每次都会有人怀疑会不会Swap导致的,先看看Swap,如果真的使用了Swap区域,基本上就会让Swap区域背锅了。

案例一:CMS GC时间飙升

有次群里有人给出一个case:CMS GC时间飙升,主要是remark阶段的处理时间太长,给出的日志如下:

image.png

群里有人使用到了Swap区域。而对CMS GC如果有了解的话,想问的肯定是是不是加了“-XX:+CMSScavengeBeforeRemark”这个参数。在明确告知有这个参数。但是我在这个remark阶段并没有看到young gc,提问者就又截了一个全一点的日志,如下:

image.png

感觉日志在remark之前并没有进行young gc,正常的加入“-XX:+CMSScavengeBeforeRemark”参数日志应该如下:

image.png

于是又让提问者确认一下,在remark时间正常的情况下是不是进行了young gc,在remark时间异常的情况下没有进行young gc,得到的答案是。那么问题基本上就转化成了“为什么在remark之前没有进行young gc呢”

然后带着这个问题去Google基本上就比较容易找到答案了,基本可以概述为在执行JNI时候,有可能会导致JVM阻止执行young gc。可以参考:

答案一

答案二

加入参数:+PrintJNIGCStalls可以验证该问题。

案例二:Young GC 时间飙升很高

这个笔者经历的一个case,监控&日志如下:

image.png

image.png

因为gc log中只能看到GC总时间看不到哪个阶段出现问题,所以把垃圾回收齐切换到G1,看到的log如下:

image.png

发现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工具等有较为深入的了解。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK