故障排除JVM GC长暂停
#java #jvm #springcloud

最初,在线某个应用程序的垃圾收集中存在异常,并且该应用程序中的某些实例经历了特别长的完整GC时间,持续约15-30秒。平均而言,它发生一次每两周一次。

Image description

Image description

JVM参数配置:

-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M

Image description

  1. 分析GC日志。 GC日志记录了每个GC的执行时间和结果。通过分析GC日志,您可以优化堆和GC设置,或改进应用程序的对象分配模式。

在这种情况下,完整GC的原因是人体工程学,因为启用了useadaptivesizepolicy,并且JVM正在调整和调整自身,导致完整的GC。

此日志主要反映GC之前和之后的更改,但目前尚不清楚导致问题的原因。

Image description

要启用GC日志,需要添加以下JVM启动参数:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/export/log/risk_pillar/gc.log

常见的年轻GC和完整GC日志的含义如下:

Image description

  1. 进一步研究服务器性能指标。 获得GC执行时间后,请通过监视平台调查此时间点异常值的指标。最终发现,大约5:06(GC的时间),CPU的使用情况大大增加,而交换显示了资源的释放和记忆资源增长的转折点。

Image description

JVM是否使用交换?
CPU使用情况的突然增加以及由GC引起的掉期空间的释放?
为了验证JVM是否使用交换,我们检查了“ proc”目录下的过程内存资源的使用。

for i in (cd/proc;ls∣grep"[0−9]"∣awk′0 >100');
do awk '/Swap:/{a=a+2}END{print '"i"',a/1024"M"}' /proc/$i/smaps 2>/dev/null;
done | sort -k2nr | head -10 

HEAD -10“表示使用高内存使用量检索前10个进程。输出的第一列代表过程ID,第二列表示该过程的互换使用的大小。我们可以看到确实存在一个过程305MB交换空间。

Image description

这是关于互换的简要介绍:

交换是指交换分区或文件,该分区或文件主要用于在内存使用压力时触发内存回收。此时,内存中的某些数据可以交换到交换空间,以使系统不会用完记忆并导致OOM或其他致命情况。

当一个进程从操作系统请求内存并发现不够的内存时,OS将从内存中暂时未使用的数据并将其放在交换分区中,该过程称为“交换”。当该过程再次需要此数据并且OS发现有免费的物理内存时,它将数据将数据换成交换分区的物理内存,一个称为“交换”的过程。

要验证GC时间和掉期操作之间存在必要的关系,我调查了十多台机器,重点是长期持续时间的GC日志,并确认GC和掉期操作的时间点确实一致。<<<<<<<<<<<< /p>

此外,通过检查虚拟机的每个实例的交换参数,一个常见的现象是,具有较长完整GC的实例配置了参数vm.swappiness = 30(更大的值意味着更大的趋势使用交换)) ,而具有相对正常GC时间的实例则配置了参数vm.swappiness = 0(最大化使用交换的减少)。

可以将交换设置为0到100之间的值。它是一个Linux内核参数,可控制交换过程中内存使用的相对权重。

swappiness = 0:最大程度地使用物理记忆,然后交换空间
交换= 100:积极使用交换分区,并及时交换数据上的数据交换

相应的物理内存使用率和互换用法如下所示。

Image description

Image description

  1. 问题分析:
    当内存使用量到达水线(vm.swappiness)时,Linux将将一些临时未使用的内存数据移至磁盘交换中,以释放更多可用的内存空间。当需要交换区域中的数据时,它将回到内存中。当JVM执行垃圾收集时,它需要在相应的堆分区中穿越所用的内存。如果一部分堆内容已交换为GC期间的交换空间,则在遍历时,需要将其交换回记忆。因为它需要访问磁盘,所以它比访问物理内存要慢得多,并且GC暂停时间将很长,这可能会导致Linux在交换区域恢复时落后(内存与磁盘交换操作是非常CPU,并且系统IO密集型)。在高电流/QPS服务中,此滞后可能是致命的(STW)。

  2. 问题:
    启用交易的JVM会总是需要更长的时间才能执行GC?
    如果JVM如此不喜欢交换,为什么不禁止其使用?
    交换的工作机制是什么?该服务器具有8GB的物理内存并使用交换内存,这意味着物理内存还不够,但是根据Free命令,实际的物理内存使用情况似乎并不高,而交换已占据了近1G。

Image description

freeï¼不包括buff/cache

的剩余内存量

共享的共享内存。

buff/cacheï¼用于缓冲和缓存的内存数(通常是由程序经常访问文件引起的)。

可用剩余内存的实际数量。

5.Further的想法
一个人可能会考虑禁用交换磁盘缓存的含义。

实际上,没有必要如此激进。重要的是要注意,世界绝不只是二进制,每个人都倾向于选择介于两者之间的某个地方。有些倾向于0,而另一些则朝1。

很明显,关于掉期问题,JVM可以选择最大程度地减少其用法以减少其影响。了解Linux内存恢复如何工作以减少任何可能的问题很重要。

让我们首先看一下如何触发交换。

Linux在两个方案中触发内存恢复:​​当存储器分配期间没有足够的自由存储器,以及守护程序进程(KSWAPD进程)定期检查系统的内存并启动内存恢复时,当可用内存降至特定阈值以下时。

6.Sculation
由于GC在实时服务中的间隔很短,因此内存中的事物没有机会交换并在GC期间立即恢复。执行GC时,交换分区的数据无需将其交换回物理内存,而是完全基于内存计算的,这使其更快。用于交换交换分区的内存数据的选择策略可能与LRU算法相似(最近使用的最少)。

适当降低堆大小也可以解决问题。

这也间接表明,在Linux系统上部署Java服务时,内存分配不应简单地大且全面,而应考虑Java永久一代JVM的内存要求,Java Heap(年轻和旧世),线程在不同的情况下堆栈和爪哇nio。

7.结论
总之,当互换和GC同时发生时,GC时间将很长,会导致严重的JVM口吃,在极端情况下,服务崩溃了。

主要原因是当JVM执行GC时,它需要穿越相应的堆分区的使用的内存。如果在GC时已将堆的一部分换成交换,则在穿越此部分时,必须将其交换回记忆。在更极端的情况下,如果由于记忆空间不足而需要将内存中的另一部分交换以交换,则整个堆分区将在遍历堆分区的过程中写入交换,从而导致GC过长的GC时间。交易区域的大小应在线限制,如果交换使用率很高,则应对其进行调查和解决。如果适当的话,可以降低堆尺寸或可以添加物理内存。

因此,在Linux系统上部署Java服务时,重要的是要谨慎分配。