当前位置: 首页 > news >正文

故障排查详解

故障排查详解

本章导读

系统故障不可避免,但快速定位和解决问题的能力决定了系统的可用性。本章系统讲解OOM、CPU飙升、死锁等常见故障的排查方法与工具使用,帮助读者建立完整的故障排查体系,从"盲人摸象"进化到"精准定位"。

学习目标

  • 目标1:掌握JDK诊断工具(jstack、jmap、jstat)的使用方法
  • 目标2:能够分析堆转储文件定位内存泄漏根因
  • 目标3:掌握CPU飙升问题的定位流程与常见原因分析
  • 目标4:能够检测和诊断线程死锁并制定预防策略
  • 目标5:建立系统化的故障排查思维框架与应急预案

前置知识:熟悉JVM内存模型与GC机制,了解多线程编程基础,具备Linux运维经验

阅读时长:约 50 分钟

一、知识概述

故障排查是保障系统稳定运行的核心能力。当系统出现异常时,能够快速定位问题根因并采取有效措施,是每个开发和运维人员必备的技能。本文将详细介绍Java应用常见故障的排查方法和工具使用。

1.1 故障排查体系

┌─────────────────────────────────────────────────────────────┐ │ 故障排查体系 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. 故障发现 │ │ └── 监控告警、用户反馈、日志异常 │ │ │ │ 2. 故障定位 │ │ └── 分析症状、收集信息、定位原因 │ │ │ │ 3. 故障处理 │ │ └── 临时方案、根本解决、验证效果 │ │ │ │ 4. 故障复盘 │ │ └── 总结原因、改进措施、文档沉淀 │ │ │ │ 常见故障类型: │ │ ┌─────────────┬─────────────────────────────────────┐ │ │ │ OOM │ 内存溢出,应用崩溃 │ │ │ ├─────────────┼─────────────────────────────────────┤ │ │ │ CPU飙升 │ CPU占用过高,响应缓慢 │ │ │ ├─────────────┼─────────────────────────────────────┤ │ │ │ 死锁 │ 线程阻塞,功能不可用 │ │ │ ├─────────────┼─────────────────────────────────────┤ │ │ │ 响应慢 │ 接口超时,用户体验差 │ │ │ ├─────────────┼─────────────────────────────────────┤ │ │ │ 连接异常 │ 数据库/缓存连接失败 │ │ │ └─────────────┴─────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘

1.2 故障排查工具箱

┌─────────────────────────────────────────────────────────────┐ │ 故障排查工具箱 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ JDK工具 │ │ ├── jps - 查看Java进程 │ │ ├── jstat - JVM统计信息 │ │ ├── jinfo - 查看JVM配置 │ │ ├── jmap - 内存映射、堆转储 │ │ ├── jstack - 线程堆栈 │ │ ├── jcmd - 多功能诊断工具 │ │ └── jconsole - 图形化监控 │ │ │ │ 系统工具 │ │ ├── top/htop - 系统资源监控 │ │ ├── vmstat - 虚拟内存统计 │ │ ├── iostat - IO统计 │ │ ├── netstat - 网络连接 │ │ ├── lsof - 打开的文件 │ │ ├── strace - 系统调用跟踪 │ │ └── perf - 性能分析 │ │ │ │ 高级工具 │ │ ├── Arthas - 阿里开源诊断工具 │ │ ├── JProfiler - 商业性能分析工具 │ │ ├── MAT - 内存分析工具 │ │ └── async-profiler - 异步性能分析 │ │ │ └─────────────────────────────────────────────────────────────┘

二、OOM分析与排查

2.1 OOM类型与原因

┌─────────────────────────────────────────────────────────────┐ │ OOM类型与原因 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Java heap space │ │ 原因:堆内存不足,对象过多无法回收 │ │ 解决:增大堆内存、分析内存泄漏、优化代码 │ │ │ │ 2. Metaspace / PermGen │ │ 原因:类加载过多、动态代理、反射使用过度 │ │ 解决:增大元空间、限制类加载、优化框架配置 │ │ │ │ 3. GC overhead limit exceeded │ │ 原因:GC时间过长,无法有效回收内存 │ │ 解决:优化GC参数、分析内存泄漏 │ │ │ │ 4. Direct buffer memory │ │ 原因:NIO直接内存不足 │ │ 解决:增大直接内存限制、检查NIO使用 │ │ │ │ 5. Out of swap space │ │ 原因:系统交换空间不足 │ │ 解决:增加系统内存、调整内存使用 │ │ │ │ 6. Requested array size exceeds VM limit │ │ 原因:请求的数组大小超过JVM限制 │ │ 解决:优化业务逻辑,避免创建超大数组 │ │ │ └─────────────────────────────────────────────────────────────┘

2.2 堆内存分析

# ============================================# 查看Java进程# ============================================jps-lvm# 输出示例:# 12345 myapp.jar -Xms2g -Xmx2g -XX:+UseG1GC# ============================================# 查看堆内存使用情况# ============================================jstat-gc1234510005# 输出说明:# S0C S1C S0U S1U EC EU OC OU MC MU# 512.0 512.0 0.0 128.0 8192.0 4096.0 16384.0 12288.0 8192.0 7168.0## S0C/S1C: Survivor区容量# S0U/S1U: Survivor区使用量# EC/EU: Eden区容量/使用量# OC/OU: 老年代容量/使用量# MC/MU: 元空间容量/使用量# ============================================# 生成堆转储文件# ============================================# 方式1: 使用jmapjmap-dump:format=b,file=heap.hprof12345# 方式2: 使用jcmdjcmd12345GC.heap_dump /tmp/heap.hprof# 方式3: 应用启动时自动生成(推荐生产环境)# -XX:+HeapDumpOnOutOfMemoryError# -XX:HeapDumpPath=/logs/heapdump.hprof# ============================================# 查看堆内存直方图# ============================================jmap-histo:live12345|head-30# 输出示例:# num #instances #bytes class name# ----------------------------------------------# 1: 50000 4000000 com.example.User# 2: 30000 2400000 java.lang.String# 3: 20000 1600000 java.util.HashMap$Node# ============================================# 使用MAT分析堆转储# ============================================# 1. 下载MAT: https://eclipse.dev/mat/# 2. 打开heap.hprof文件# 3. 查看Dominator Tree(支配树)# 4. 分析Leak Suspects(内存泄漏嫌疑)# 5. 查看Histogram(对象统计)

2.3 内存泄漏排查实战

// ============================================// 常见内存泄漏场景// ============================================// 场景1: 静态集合持有对象引用publicclassMemoryLeakDemo{// 静态集合会一直持有对象引用,导致无法被GCprivatestaticfinalMap<String,Object>CACHE=newHashMap<>();publicvoidaddToCache(Stringkey,Objectvalue){CACHE.put(key,value);// 如果不删除,会一直占用内存}// 正确做法:使用弱引用或定期清理privatestaticfinalMap<String,Object>WEAK_CACHE=newWeakHashMap<>();}// 场景2: 监听器未注销publicclassEventSource{privateList<EventListener>listeners=newArrayList<>();publicvoidaddListener(EventListenerlistener){listeners.add(listener);}// 忘记提供remove方法,导致监听器无法被回收// public void removeListener(EventListener listener) {// listeners.remove(listener);// }}// 场景3: ThreadLocal未清理publicclassThreadLocalLeak{privatestaticfinalThreadLocal<byte[]>THREAD_LOCAL=newThreadLocal<>();publicvoidprocess(){THREAD_LOCAL.set(newbyte[1024*1024]);// 1MB// 忘记清理// THREAD_LOCAL.remove();}}// 场景4: 数据库连接未关闭publicclassConnectionLeak{publicvoidquery(){Connectionconn=null;try{conn=dataSource.getConnection();// 执行查询}finally{// 忘记关闭连接// if (conn != null) conn.close();}}}// 场景5: 缓存无限增长publicclassCacheLeak{privateMap<String,CacheEntry>cache=newHashMap<>();publicvoidput(Stringkey,Objectvalue){cache.put(key,newCacheEntry(value,System.currentTimeMillis()));// 没有清理过期缓存的机制}// 正确做法:使用Caffeine等缓存框架// private Cache<String, Object> cache = Caffeine.newBuilder()// .maximumSize(10000)// .expireAfterWrite(Duration.ofMinutes(30))// .build();}
// ============================================// 内存泄漏排查示例// ============================================// 使用Arthas排查内存泄漏// 1. 启动Arthas// java -jar arthas-boot.jar// 2. 选择目标进程// 选择对应的Java进程编号// 3. 查看内存使用情况// dashboard// 4. 查看对象统计// heap
http://www.cnnetsun.cn/news/2020797.html

相关文章:

  • CSS如何解决移动端布局塌陷问题_使用伪元素clearfix修复浮动.txt
  • 时序攻击原理与防御:从RSA到AES的侧信道安全实践
  • 消除二叉树中的节点(python)
  • 打印时隐藏元素_print样式display-none技巧【操作】
  • 渗透基础知识ctfshow——Web应用安全与防护(第六 七章)
  • 【12.MyBatis源码剖析与架构实战】3.Executor源码剖析
  • Python监控文件夹变动情况_watchdog库实现文件实时审计
  • 英雄联盟国服皮肤定制终极指南:R3nzSkin技术深度解析与安全实践
  • Playwright爬虫进阶:如何用`page.route`拦截请求、模拟登录并高效监听API数据?
  • 如何在新电脑上正确运行已部署的 Django 项目
  • 如何5分钟将B站视频转为文字?bili2text开源工具完全指南
  • 数据结构学习记录:树 + 二叉树 + 堆 从原理到手撕代码
  • PCIe 6.0的Shared Flow Control到底怎么玩?用Credit Block解决Buffer管理难题
  • Dify API跨域(CORS)配置失效终极解法:从nginx proxy_pass到FastAPI middleware的4层拦截点对照表(含Docker Compose完整配置片段)
  • 别再只会用`uvm_object_utils`了!拆解宏定义,搞懂UVM工厂注册的底层逻辑
  • 手把手教你用C++实现LR(1)语法分析器(附完整代码及避坑指南)
  • 隐形Unicode技巧:新型JavaScript混淆方法被用于针对美国PAC附属机构的网络钓鱼攻击
  • 别只用来抓包了!Burp Suite的Filter、Comparer和Decoder模块,帮你高效分析漏洞与调试API
  • Codex智能编程:告别重复造轮子
  • 杰理之一拖八工具烧录介绍【篇】
  • 从Ext4迁移到Btrfs实战:我的个人服务器数据无损转换全记录与避坑指南
  • OpencvSharp 算子学习教案之 - Cv2.Erode
  • 从GDC论文到UE5蓝图:手把手实现‘惯性化’动画过渡,让你的角色动作更物理
  • QtDataVisualization实战:用C++快速打造一个可交互的3D图表演示器(附完整源码)
  • GLM-4.1V-9B-Base应用场景:在线教育题图自动解析与知识点标注
  • 从收音机到5G:三极管频率特性模型演变史,以及它为什么今天依然重要
  • 手把手复现:用Python和OpenCV一步步理解Marr视觉计算理论的“要素图”
  • 别再手动仿真了!手把手教你配置Vivado 2018.3与ModelSim SE的联合仿真环境
  • 网络‘活地图’实战:用PyHPEcw7库+D3.js打造可点击的拓扑仪表盘
  • 首文双拼的衍生版:首嵋双拼