JVM性能调优:从定位问题到解决——线上CPU 100%怎么办?
上回说到 并发锁有个小伙伴问”老师生产环境CPU 100%接口响应超时该如何排查”这让我想起了小王的一次线上事故——大促期间服务CPU飙到100%接口响应时间从500ms飙升到30s。今天我们就来聊聊 JVM性能调优从问题定位到解决方案。一、问题现场还原那是一个大促的晚上监控告警【紧急】订单服务CPU 100% 【紧急】接口响应时间 P99 30000ms 【紧急】Full GC频率1次/分钟小王赶紧登录服务器执行top命令PID USER PR NI VIRT RES %CPU %MEM TIME COMMAND 12345 appuser 20 0 3.2g 1.8g 100 45.2 2:30.52 javaCPU 100%这是什么情况二、问题排查步骤2.1 第一步找到占用CPU最高的线程# 1. 查看Java进程PID jps # 输出12345 OrderService # 2. 查看占用CPU最高的线程-H显示线程-p指定PID-n显示3次-d间隔1秒 top -H -p 12345 -n 3 -d 1 # 输出 PID USER PR NI VIRT RES %CPU %MEM TIME COMMAND 12348 appuser 20 0 3.2g 1.8g 89.5 45.2 1:25.30 java 12349 appuser 20 0 3.2g 1.8g 10.5 45.2 0:15.20 java 12350 appuser 20 0 3.2g 1.8g 0.0 45.2 0:00.00 java # 12348线程占用89.5%的CPU2.2 第二步将线程PID转换为16进制# 将12348转换为16进制 printf %x\n 12348 # 输出303c2.3 第三步查看线程堆栈# 查看线程堆栈-l打印锁信息 jstack 12345 | grep 303c -A 20 # 输出 pool-1-thread-1 #21 prio5 os_prio0 tid0x00007f8c5c012800 nid0x303c runnable [0x00007f8c52016000] java.lang.Thread.State: RUNNABLE at java.util.HashMap$TreeNode.transfer(HashMap.java:2015) at java.util.HashMap.resize(HashMap.java:703) at java.util.HashMap.putVal(HashMap.java:662) at java.util.HashMap.put(HashMap.java:611) at com.example.service.OrderService.processOrder(OrderService.java:125) at com.example.controller.OrderController.createOrder(OrderController.java:45)发现问题线程卡在HashMap.resize()上2.4 第四步分析问题// 问题代码 public class OrderService { private MapLong, Order orderCache new HashMap(); public void processOrder(Order order) { // 大量并发调用put导致HashMap扩容 orderCache.put(order.getId(), order); } }问题原因HashMap不是线程安全的多线程并发put会导致死循环JDK 1.7或者导致CPU 100%JDK 1.8扩容导致三、解决方案3.1 方案一使用ConcurrentHashMap推荐public class OrderService { // 使用ConcurrentHashMap替代HashMap private MapLong, Order orderCache new ConcurrentHashMap(); public void processOrder(Order order) { orderCache.put(order.getId(), order); } }3.2 方案二使用synchronizedpublic class OrderService { private MapLong, Order orderCache new HashMap(); public synchronized void processOrder(Order order) { orderCache.put(order.getId(), order); } }3.3 方案三使用Collections.synchronizedMappublic class OrderService { private MapLong, Order orderCache Collections.synchronizedMap(new HashMap()); public void processOrder(Order order) { orderCache.put(order.getId(), order); } }四、常见JVM问题及解决方案4.1 问题一内存溢出OOM现象java.lang.OutOfMemoryError: Java heap space排查步骤# 1. 查看JVM参数 jps -v # 输出12345 OrderService -Xms1g -Xmx1g # 2. 查看堆内存使用情况 jmap -heap 12345 # 3. 导出堆dump文件 jmap -dump:formatb,fileheap.hprof 12345 # 4. 使用MAT分析dump文件 # 下载地址https://www.eclipse.org/mat/解决方案# 调整JVM堆内存大小 java -Xms2g -Xmx2g -jar app.jar # 或者 java -XX:UseG1GC -Xms2g -Xmx2g -jar app.jar4.2 问题二内存泄漏现象堆内存持续增长Full GC频繁最终OOM常见原因// 原因1静态集合 public class MemoryLeakDemo { private static ListObject cache new ArrayList(); // 永不释放 public void add(Object obj) { cache.add(obj); } } // 原因2未关闭的资源 public void readFile() { InputStream is new FileInputStream(file.txt); // 忘记关闭is } // 原因3ThreadLocal未清理 public class ThreadLocalDemo { private static ThreadLocalObject threadLocal new ThreadLocal(); public void set(Object obj) { threadLocal.set(obj); // 线程结束后未清理导致内存泄漏 } }解决方案// 解决方案1使用弱引用 private static MapObject, Object cache new WeakHashMap(); // 解决方案2使用try-with-resources try (InputStream is new FileInputStream(file.txt)) { // 使用is } catch (IOException e) { e.printStackTrace(); } // 解决方案3ThreadLocal使用后清理 threadLocal.set(obj); try { // 使用threadLocal } finally { threadLocal.remove(); // 清理 }4.3 问题三频繁Full GC现象[Full GC (Allocation Failure) [PSYoungGen: 2048K-0K(2560K)] [ParOldGen: 69632K-69632K(69632K)]排查步骤# 查看GC日志 tail -f /var/log/gc.log # 查看GC统计信息 jstat -gcutil 12345 1000 10解决方案# 1. 使用G1垃圾收集器推荐 java -XX:UseG1GC -Xms2g -Xmx2g -jar app.jar # 2. 调整年轻代和老年代比例 java -Xms2g -Xmx2g -XX:NewRatio2 -jar app.jar # 3. 调整 survivor 比例 java -Xms2g -Xmx2g -XX:SurvivorRatio8 -jar app.jar五、JVM参数调优5.1 核心参数# 堆内存 -Xms2g # 初始堆大小 -Xmx2g # 最大堆大小 # 年轻代 -Xmn1g # 年轻代大小 -XX:NewRatio2 # 年轻代与老年代比例2:1 -XX:SurvivorRatio8 # Eden与Survivor比例8:1 # 垃圾收集器 -XX:UseG1GC # 使用G1推荐 -XX:UseParallelGC # 使用Parallel GC -XX:UseConcMarkSweepGC # 使用CMSJDK 14已废弃 # GC日志 -Xlog:gc*:filegc.log:time,tags:filecount5,filesize10m # 其他 -XX:MaxDirectMemorySize1g # 直接内存大小 -XX:MetaspaceSize256m # 元空间大小5.2 不同场景的推荐参数# 场景1中小型应用1-2GB内存 java -Xms1g -Xmx1g -XX:UseG1GC -jar app.jar # 场景2大型应用4-8GB内存 java -Xms4g -Xmx4g -XX:UseG1GC -XX:MaxGCPauseMillis200 -jar app.jar # 场景3高并发应用16GB内存 java -Xms8g -Xmx8g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:ParallelGCThreads8 -jar app.jar六、监控工具6.1 JDK自带工具# jps查看Java进程 jps -v # jstat查看JVM统计信息 jstat -gcutil pid 1000 10 # jmap查看堆内存 jmap -heap pid # jstack查看线程堆栈 jstack pid # jcmd多功能工具 jcmd pid VM.flags jcmd pid GC.heap_info6.2 第三方工具工具用途下载地址Arthas在线诊断https://arthas.aliyun.com/VisualVM监控分析https://visualvm.github.io/MAT内存分析https://www.eclipse.org/mat/JProfiler性能分析https://www.ej-technologies.com/products/jprofiler/七、Arthas实战7.1 安装Arthas# 下载 curl -O https://arthas.aliyun.com/arthas-boot.jar # 启动 java -jar arthas-boot.jar # 选择进程 [INFO] arthas-boot version: 3.7.1 [INFO] Found existing java process, please choose one and hit RETURN. * [1]: 12345 OrderService7.2 常用命令# 查看线程 thread thread -n 3 # 查看CPU占用最高的3个线程 # 查看类加载 sc -d *Order* # 查看方法调用 monitor -c 5 com.example.OrderService processOrder # 查看堆栈 stack com.example.OrderService processOrder # 查看属性 getstatic com.example.OrderService cache # 热更新代码 jad --source-only com.example.OrderService /tmp/OrderService.java mc /tmp/OrderService.java redefine /tmp/OrderService.class八、性能调优清单✅ JVM调优检查清单 ├── 1. 是否设置了合理的堆内存大小 │ └── 建议Xms和Xmx设置相同值避免动态扩容 ├── 2. 是否选择了合适的垃圾收集器 │ └── 建议G1通用、Parallel高吞吐、ZGC低延迟 ├── 3. 是否开启了GC日志 │ └── 建议Xlog:gc*:filegc.log ├── 4. 是否有内存泄漏 │ └── 建议定期使用MAT分析dump文件 ├── 5. 是否有死锁 │ └── 建议jstack检测死锁 ├── 6. 是否有频繁Full GC │ └── 建议调整堆大小或垃圾收集器参数 └── 7. 是否有CPU 100% └── 建议top jstack定位问题线程九、总结今天我们学到了要点说明CPU 100%top jstack定位问题线程内存溢出jmap MAT分析dump文件内存泄漏静态集合、未关闭资源、ThreadLocal频繁Full GC调整堆大小、选择合适的GCJVM参数Xms/Xmx、垃圾收集器、GC日志监控工具jps/jstat/jmap/jstack、Arthas彩蛋小王用ConcurrentHashMap替代HashMap后CPU从100%降到20%接口响应时间恢复正常。他在群里发消息“HashMap就像多人抢厕所——谁先进去谁先上不排队容易打架。ConcurrentHashMap就像智能厕所——有多个隔间还有排队系统秩序井然”
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2557067.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!