JVM调优 - 带你无痛云排查解决K8s的Pod内存使用率接近99%告警(JVM堆分配参数-Xmx原理)

人心惶惶:一个没有故障的告警

在这里插入图片描述

K8s Pod长期内存占用99%,触发了告警。而实际并没有什么人反馈出现问题,遇到这种你慌不慌?
Pod内存限制 15.6G,实际使用 15.5G,究竟是为什么呢?我将以一步步带你无痛(保证观看轻松)“云排查”

奇怪?JVM Heap 正常!

我们需要将运行情况 dump 下来,具体需要进入k8s pod中,然后进行操作。

/bin/sh

% jps
6 jar
236554 Jps

% jmap -dump:live,format=b,file=/tmp/dump.hprof 6    
Dumping heap to /tmp/dump.hprof ...
Heap dump file created

在这里插入图片描述

在这里插入图片描述
没有想象中那么大的内存占用,可能是误报?

JVM 内存监控,发现内存占用并不大。
在这里插入图片描述

其实 Eden 一直在波动,其他的内存占用也不多,为什么那条内存占用监控曲线一直在高位接近99%呢?

container_memory_working_set_bytes:存占用指标不同罢了

所以这个 pod memory usage 是不是有问题,我们追溯看看监控的指标到底是什么?还有,是不是误报?是不是监控坏了?

我们看到,我们用的是 VictoriaMetrics 的监控指标。我们拿到其分析语句,内存占用大小指标是 container_memory_working_set_bytes,我们是通过 其与 kube_pod_container_resource_limits 的比值 > 0.85 来显示曲线的,所以分析这条曲线的底层逻辑,也就可以确认内存占用率 99% 的原因了。

问题只能出现在分母身上,难道是 container_memory_working_set_bytes 不准确?但是网上并没有对其准确性的怀疑的信息,而且加上曲线各有各的大小所以不可能是监控坏了,我们可以大致认为内存占用是确实存在的,是看的内存占用指标不同!
在这里插入图片描述

sum by(container, pod) (container_memory_working_set_bytes{cluster="aliyun-dsl-k8s-prod",namespace="$namespace",pod=~"$deploy_name.*", container!="", image!="", container!="POD"})
/sum by(container, pod) (kube_pod_container_resource_limits{resource="memory", namespace="$namespace",pod=~"$deploy_name.*"})
>0.85

在这里插入图片描述

揪出罪魁祸首:Linux cache/buff 缓存机制

## 查看 linux 内存使用率
free -m
## 查看线程实时情况
top

在这里插入图片描述

执行 free -m 指令,发现pod的 cache/buff 占比较高,而 cache/buffLinux 的缓存机制,这部分内存其实在被需要的时候就会释放,真实的可用空间 = free + buff/cache 可用部分,换句话说其实JVM堆实际可用内存还是很多的
在这里插入图片描述
top 指令,发现 RES = 15.5g,这里其实就把 Java 进程的 buff/cache 里的内容当成了占用的内存资源(其实真实也是占用了,只是支持随时释放而已)

通过 ps aux | grep java 指令查看 JVM 启动参数(只给出部分)

# # # # ps aux | grep java
root           1  0.0  0.0   2528   676 ?        Ss   09:24   0:00 sh -c java -Xms1g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m $JVM_OPTS $JAVA_OPTS $SKYWALKING_OPTS -javaagent:/usr/local/skywalking/skywalking-agent.jar -javaagent:/usr/local/jmx/jmx_prometheus_javaagent-0.18.0.jar=12345:/usr/local/jmx/config.yaml  -jar /app.jar $APP_OPTS
root           6 98.4 12.5 26977188 16212696 ?   Sl   09:24 531:48 java -Xms1g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Duser.timezone=Asia/Shanghai -Djava.security.egd=file:/dev/./urandom -Xms14g -Xmx14g -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=15 -XX:InitiatingHeapOccupancyPercent=45 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=4 -XX:MaxGCPauseMillis=200 -XX:+ExitOnOutOfMemoryError ..

我们发现了 -Xms14g -Xmx14g 参数。

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.3
The heap is created on virtual machine start-up. Heap storage for objects is reclaimed by an automatic storage management system (known as a garbage collector); objects are never explicitly deallocated. The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor’s system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous.
翻译:
堆在虚拟机启动时创建。对象的堆内存由自动内存管理系统(即垃圾回收器)负责回收;对象永远不会被显式释放。Java 虚拟机规范不限定自动内存管理系统的具体实现方式,开发者可根据系统需求选择内存管理技术。堆的大小可以是固定的,也可以根据计算需求动态扩展,若不再需要较大的堆,也可动态收缩。堆的内存空间无需连续。

根据 Java8虚拟机规范,其实堆是被“创建”的,而且内存管理方式不限,所以一切都是可以理解的。只不过具体虚拟机实现还需要去好好探究,后续再说,我们至少找到了问题所在。

结论:-Xms -Xmx 规定了堆大小最大、最小为14g(不同虚拟机有不同实现),这可能就是导致 buff/cache(heap+meta等) 一直在 16g 附近的原因,因为 JVM 可能会使用 Linux 的缓冲机制来提升效率,GC回收的对象会从 used 移动到 buff/cache,但进程的 RES 占用并没有减少。
当应用启动后,K8s Pod 内存会因为对象创建而越来越高,在到达 -Xmx 这个水位后,就会开始使用 buff/cache(移动到 used),Pod 内存占用率一直维持在 16g,所以才会一直告警,曲线也不降

在这里插入图片描述

解决:后续 -Xms 、-Xmx 都改为了 13g,曲线下降,也保证了内存的稳定、不超限告警、以及预留足够的物理内存给其他应用。

在这里插入图片描述

参考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ergonomics.html#sthref5
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值