本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
理论和实践结合
JVM调优关注点
- GC日志情况
- 线程栈运行状态和情况
- 内存使用情况
GC日志解读和分析
-XX:+PrintGCDetail :打印GC日志
-XX:+PrintGCDateStaps:打印gc日志带时间
-Xloggc: GC日志打印到文件中
java运行内存:如果物理内存<1G,默认和物理内存1/2,物理内存>8G,默认是物理内存的1/4.
-Xmx -Xms 设置一样: GC的频繁的开辟空间 堆内存的大小是蓄水池的作用
-
分析GC日志的工具
-
GCEasy 在线工具 :gceasy.io
-
GCViewer:jar包
产生GC的原因
Allocation Failure -- 分配内存失败
Ergonomics -- 在JVM中的垃圾收集器中的Ergonomics就是负责自动的调解gc暂停时间和吞吐量之间的平衡,然后你的虚拟机性能更好的一种做法。 #发现当我们使用Server模式下的ParallelGC收集器组合(Parallel Scavenge+Serial Old的组合)下,担保机制的实现和之前的Client模式下(SerialGC收集器组合)有所变化。在GC前还会进行一次判断,如果要分配的内存>=Eden区大小的一半,那么会直接把要分配的内存放入老年代中。否则才会进入担保机制。 #bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes; 晋升到老生代的平均大小大于老生代的剩余大小,则会返回true,认为需要一次full gc
日志解读实践
[GC (Allocation Failure) [PSYoungGen: 65536K->10750K(76288K)] 65536K->22997K(251392K), 0.0047065 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
#分配内存失败,产生一次YoungGC 4毫秒内(GC暂停时间)垃圾回收Young区的内存从65M压缩到10M。压缩了55M。整个Young区最大的容量是762M。整个堆内存的情况从65M压缩到22M,压缩了43M。此次GC过程中有12MYoung区的数据从Young区晋升到Old区。
......
[Full GC (Ergonomics) [PSYoungGen: 10735K->0K(272896K)] [ParOldGen: 200381K->178109K(352256K)] 211117K->178109K(625152K), [Metaspace: 2621K->2621K(1056768K)], 0.0312855 secs] [Times: user=0.19 sys=0.00, real=0.03 secs]
#产生FullGC的原因是:Ergonomics -- 在JVM中的垃圾收集器中的Ergonomics就是负责自动的调解gc暂停时间和吞吐量之间的平衡,然后你的虚拟机性能更好的一种做法。
#发现当我们使用Server模式下的ParallelGC收集器组合(Parallel Scavenge+Serial Old的组合)下,担保机制的实现和之前的Client模式下(SerialGC收集器组合)有所变化。在GC前还会进行一次判断,如果要分配的内存>=Eden区大小的一半,那么会直接把要分配的内存放入老年代中。否则才会进入担保机制。
#bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes; 晋升到老生代的平均大小大于老生代的剩余大小,则会返回true,认为需要一次full gc
#Ergonomics产生一次FullGC 31毫秒内(GC暂停时间)垃圾回收Young区的内存从10M压缩到0M(进行FullGC的时候直接把Young区的数据清掉了)。Old区从200M压缩到178M,Old区的容量是352M。整个堆内存的情况从211M压缩到178M,压缩了33M。
......
执行结束!共生成对象次数:14863
JVM堆栈数据分析
JVM线程模型:
JVM内部线程
VM线程:单例VMThread对象
定时任务线程:WatcherThread
GC线程:并行和并发垃圾回收
编译器线程:字节编译机器代码
信号分发线程:进程指示信号
JVM支持多种方式进行线程存储
JVM工具
JDK工具:jstack jcmd jconsole jvisualvm
Shell命令或者控制台,kill -3
JMX技术:主要使用ThreadMxBean
在线分析:fastthread.io/
注:java对象:对象头12字节、对象体 4的倍数补齐
jmap -heap 内存情况
jmap -dump 备份下内存快照
内存泄露:未释放对象引用
OOM java heap space:堆内存空间不足以存放新的对象
产生原因:大对象、超过预期的访问量或者数据量、内存泄露
OOM:PermGen space
解决方法:增加PermGen、MetaSpace空间
OOM:Unable to create new natice thread 错误创建线程数量超过上限
解决思路:
1、调整参数 ulimit -a
2、降低xss等参数
3、调整代码,改变线程创建和使用方式
JVM问题分析调优经验
1、高分配速率 Hign Allocation Rate 表示单位时间内分配的内存量。(上次GC之后留存的新增代的对象 到这一次GC发生之前整个年轻代对象相减 除以 gc间隔时间)
正常系统:分配速率低~回收速率 - >健康
内存泄漏: 分配速率持续大于回收速率 -> OOM
性能劣化: 分配速率很高 ~回收速率 ->亚健康
TODO-实践 看下线上环境分配速率问题
2、过早提升
提升速率 promotion rate 用于衡量单位时间内从年轻代提升到老年代的数据量
1、短时间内频繁执行fullGC
2、每次fullGC后老年代的使用率都很低
3、提升速率接近分配速率
解决方案:
1、增加年轻代的大小,设置jvm启动参数 -XX:NewSize
2、减少每次业务处理使用的内存数量
JVM疑难情况分析
Arthas诊断分析工具
问题定位思路或者方向
- 查询业务日志
- 系统资源和监控平台(CPU负载、内存不足、磁盘使用量、硬件故障)
- 网络问题:流量打满、相应超时、网络抖动
- 性能指标:实时监控、历史数据、假死、卡顿、响应慢
- 排查业务系统日志
- APM:链路
- 配置文件:启动参数、jvm监控、数据库参数、内存问题、GC问题
- 资源竞争、坏邻居效应
- dump线程内存情况,抽样分析、异步化、削峰填谷