1
头图
三步走:认识问题,分析问题,解决问题

一、认识问题

【业务背景】某个Java业务应用,所有容器实例minor GC频繁,major GC偶发,每天重启一次。

现象是业务每隔30分钟会从商品域拉取全量商品和SKU数据,更新到缓存中。业务侧短期内不好解决这个

二、分析问题

JVM参数配置
【调优前】2GB内存,使用G1GC,最大元数据空间大小是512MB

-Xms2048m -Xmx2048m -XX:MaxMetaspaceSize=512m -XX:ThreadStackSize=256 -XX:+DisableExplicitGC -XX:+UseG1GC 

Quick Facts 速览

在这里插入图片描述

I/O Overview 概述

http请求耗时,偶尔较长。
在这里插入图片描述

JVM Memory 内存

内存没有越界,没看出异常和端倪。
在这里插入图片描述

JVM Misc 杂项

Load,1 分钟负载数据,不好评估影响面。
Log Events,说明日志写操作比较多。
在这里插入图片描述

JVM Memory Pools (Heap) 内存池(堆内存)

G1 Eden Space 伊甸园G1 Old Gen 老年代,在2GB堆内存内你争我抢,跷跷板效应。异常情况,我们期望它们是一条笔直的直线,没有抖动与突刺。
G1 Survivor Space 幸存者空间,使用和提交的空间在38MB之内,从空间大小数据可以看出堆区域大小HeapRegionSize=1MB
在这里插入图片描述

JVM Memory Pools (Non-Heap) 内存池(非堆内存)

Metaspace 元数据空间,使用了200MB不到,最大是512MB。有优化空间
Compressed Class Space 压缩的类空间,使用了25MB,最大是504MB。有优化空间
在这里插入图片描述

Garbage Collection 垃圾收集

Collections 收集次数

end of major GC (Allocation Failure),Full GC,分配失败,每天最多发生1次;
end of minor GC (G1 Evacuation Pause),Young GC,疏散暂停,发生较频繁;
end of minor GC (G1 Humongous Allocation),巨型对象分配,发生较频繁;
end of minor GC (GCLocker Initiated GC)
end of minor GC (Metadata GC Threshold)

Pause Durations 暂停时间

avg end of major GC (Allocation Failure),Full GC,分配失败,每天最多发生1次,平均耗时在1s左右;
avg end of minor GC (G1 Evacuation Pause),Young GC,疏散暂停,发生较频繁,平均耗时在100ms以内;
avg end of minor GC (G1 Humongous Allocation),巨型对象分配,发生较频繁,平均耗时在100ms以内;
max end of major GC (Allocation Failure),Full GC,分配失败,最大耗时在1s左右;
max end of minor GC (G1 Evacuation Pause),Young GC,疏散暂停,最大耗时在[300,500]ms以内;
max end of minor GC (G1 Humongous Allocation),巨型对象分配,最大耗时在[300,500]ms以内,偶发在[700,1000]ms;
max end of minor GC (GCLocker Initiated GC)
max end of minor GC (Metadata GC Threshold)

Allocated/Promoted 已分配/已升级对象大小

allocated,已分配对象,临时对象分配较多;
promoted,已升级对象,偶尔升级到老年代;
在这里插入图片描述

Classloading 类加载

加载的类数量,没有一直递增。一切平静安好
在这里插入图片描述

Buffer Pools 缓冲池

堆外内存,使用量很少,没有泄露。
在这里插入图片描述

容器重启

容器事件,看到PodOOMKilling,pod was OOM killed.

Pod容器监控,Pod中的filebeat:7.13.4容器内存使用满了,内存是1GB。
在这里插入图片描述
在这里插入图片描述

有了上述度量遥测数据,可以针对性地做调优。

大胆假设,小心求证。

综上所述,有如下几个疑点:

  • minor GC频繁的根因是临时对象分配较多,日志输出频繁,引起频繁的Young GC。 ✅
  • major GC偶发的根因是巨型对象分配直接在老年代分配,引起Full GC。 ✅
  • 容器重启的根因是PodOOMKilling,pod was OOM killed.,同一Pod中的filebeat:7.13.4容器内存1GB使用满了。 ✅

三、解决问题

大胆假设,小心求证。

【调优后】2GB内存,使用G1GC

最大元数据空间大小是256MB,-XX:MaxMetaspaceSize=256m

堆区域大小HeapRegionSize调整为2MB,-XX:G1HeapRegionSize=2m

最大G1 Eden Space 伊甸园空间调整为1GB,-XX:MaxNewSize=1024m

-Xms2048m -Xmx2048m -XX:MaxNewSize=1024m -XX:MaxMetaspaceSize=256m -XX:ThreadStackSize=256 -XX:+DisableExplicitGC -XX:G1HeapRegionSize=2m -XX:+UseG1GC 

filebeat:7.13.4,只是优化了无用日志,没有别的动作。无用日志是罪魁祸首

几个疑点:

  • minor GC频繁的根因是临时对象分配较多,日志输出频繁,引起频繁的Young GC。 ✅
  • major GC偶发的根因是巨型对象分配直接在老年代分配,引起Full GC。 ✅
  • 容器重启的根因是PodOOMKilling,pod was OOM killed.,同一Pod中的filebeat:7.13.4容器内存1GB使用满了。 ✅

调优后的整体效果

调优前实例:192.168.112.191,过去7天的数据
调优后实例:192.168.106.45,过去1天的数据

【JVM优化对比】收集次数(Collections)暂停时间(Pause Durations)

avg end of major GC (Allocation Failure)
【调优前】每天发生一次Full GC
在这里插入图片描述
【调优后】没有发生

avg end of minor GC (G1 Evacuation Pause)
【调优前】平均耗时在[50,70]ms之间,抖动突刺频繁,偶尔超过100ms
在这里插入图片描述
【调优后】平均耗时是40ms,大部分小于50ms,收集频率是频繁了
在这里插入图片描述

avg end of minor GC (G1 Humongous Allocation)
【调优前】大对象分配失败频繁,平均耗时在[50,70]ms之间,Region大小是1MB
在这里插入图片描述
【调优后】平均耗时是40ms,收集频率是偶尔,Region大小是2MB
在这里插入图片描述

max end of major GC (Allocation Failure)
【调优前】分配失败,引起Full GC
在这里插入图片描述
【调优后】没有发生

max end of minor GC (G1 Evacuation Pause)
【调优前】平均耗时波动较大,经常[200,300]ms,偶尔耗时较高,超过500ms
在这里插入图片描述
【调优后】偶尔耗时较高,超过500ms
在这里插入图片描述

max end of minor GC (G1 Humongous Allocation)
【调优前】收集频繁,平均耗时波动较大,经常[150,300]ms
在这里插入图片描述
【调优后】平均耗时是40ms,收集频率是偶尔
在这里插入图片描述

【JVM优化对比】已分配/已升级对象大小(Allocated/Promoted)

allocated
【调优前】临时对象分配较多
在这里插入图片描述
【调优后】临时对象分配很多,是根因
在这里插入图片描述

promoted
【调优前】偶尔升级到老年代
在这里插入图片描述
【调优后】偶尔升级到老年代
在这里插入图片描述

参考引用

工欲善其事,必先利其器。

简放视野
18 声望0 粉丝

Microservices, Cloud Native, Service Mesh. Java, Go.