JVM
一、JVM组成
二、内存高-示例分析
1.示例分析
(本地设置最大堆内存,VisualVM的HeapDump 功能分析 或者基于IDEA 的 JProfiler分析)
1.在开发环境上,基于k8s 构建的服务,需要使用XXL-job调用, IJobHandler方式 (识别Bean、需要注册该服务到xxl-job)
(1)自动识别容器内存限制(即可保证你的Java进程不会因为内存问题被容器Kill。) 默认1核4G 自动操作
-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1
(2)为了尽快分析定位问题,调小内存限制,尽快抛异常。设置JVM启动参数: 手动设置(-Xms -Xmx) ```java
java -Dspring.profiles.active=dev -Xms128m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -verbose:gc -Xloggc:/data/logs/md-refresh-data-service/gc.log -jar /data/app.jar ```
注:遇到时间不同步问题(重启服务解决 或者启动ntp时间同步服务) ```java
com.xxl.rpc.util.XxlRpcException: The timestamp difference between admin and executor exceeds the limit. at ```
2.可以基本本机调式,将xxl-job 内容模拟为请求,然后通过jmeter 定时出发(并发测试);参考手动设置JVM -Xms -Xmx 方式,基于JProfiler 或VisualVM
常用方法:参考
2.分析过程:
<1.表象
生产上应用不断爆出内存溢出,造成服务自动重启,该服务业务逻辑就是对外提供接口和刷新数据到MongoDB(基于XXL-JOB bean模式的定时任务);
随着定时任务的执行,内存逐渐增加,GC少。
<2.排查
运维给出的内存使用信息(jmap -head
<3.定位
由于线上环境内存快满了,故本地模拟,复现该场景。为了尽快复现问题,本地设置启动参数为: -Xms128m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError;xxl-job 设置每分钟跑一次(多个任务都设置为1分钟),大约10分钟就抛出内存溢出。
(基于IDEA的JProfiler插件)
查看大对象信息(内存溢出一般是基于大对象引发的)
双击查看大对象详情:
展开,查看详情:
最后分析出是基于MongoDB连接引发的;由于清理备份表每次都用0开始,然后清理至保存最后几份备份表,频繁操作mongodb,最终未释放掉相应的连接信息。
<4.解决
优化方案:单独写了算法,清理除保留最近几份的表以外的几份备份表即可(通过Apollo配置方式设置)
三、CPU高,响应慢-示例分析
1.示例分析
实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep
#(1) 查询机器使用情况
top
#(2)查询单个线程(较耗资源的线程)
top -H -p 12798(pid)
#(3)jstack 查看堆内存
# 12799 的16进制字符串
print "0x%08x" 12798 # 0x000031fe
jstack 12798 |grep 31fe
#(4)查看前10行与后10行内容 => 可定位到具体代码行数
jstack 12798 |grep 31fe -A 10 -B 10
四、CPU不高,响应慢
一般是死锁的场景
一般步骤:
- jps 命令,查看java进程的pid
- jstack 查看线程日志
如果存在死锁情况,Thread Dump日志里面肯定会给出Found one Java-level deadlock:信息。只要
找到这个信息就可以立马定位到问题并且去解决。
Found one Java-level deadlock:
=============================
参考文档
- 《Java 虚拟机规范(Java SE 8版)》
- 《深入理解 Java 虚拟机 第二版》
- 《深入理解 Java 虚拟机 第三版》
- 《实战 Java 虚拟机》