Java问题排查工具箱

问题排查除了最重要的解决思路和逻辑推导能力外,工具也是不可缺少的一部分,一个好用的工具可以事半功倍,甚至在某些情况下会因为没有相应的工具而压根就没法继续进行下去,这篇文章就来讲讲在排查Java问题时通常要用到的一些工具(ps:这种文章值得收藏,看一遍其实很容易忘)。


日志相关工具

查问题的时候会非常依赖日志,因此看日志的相关工具非常重要,通常的话掌握好tail,find,fgrep,awk这几个常用工具的方法就可以,说到这个就必须说关键的异常和信息日志输出是多么的重要(看过太多异常的随意处理,例如很典型的是应用自己的ServletContextListener实现,很多的Listener实现都会变成往外抛RuntimeException,然后直接导致tomcat退出,而tomcat这个时候也不会输出这个异常信息,这种时候要查原因真的是让人很郁闷,尽管也有办法)。

日志的标准化也非常重要,日志的标准化一方面方便像我这种要查各种系统问题的人,不标准的话连日志在哪都找不到;另一方面对于分布式系统而言,如果标准化的话是很容易做日志tracing的,对问题定位会有很大帮助。


CPU相关工具

碰到一些CPU相关的问题时,通常需要用到的工具:

top (-H)

top可以实时的观察cpu的指标状况,尤其是每个core的指标状况,可以更有效的来帮助解决问题,-H则有助于看是什么线程造成的CPU消耗,这对解决一些简单的耗CPU的问题会有很大帮助。

sar

sar有助于查看历史指标数据,除了CPU外,其他内存,磁盘,网络等等各种指标都可以查看,毕竟大部分时候问题都发生在过去,所以翻历史记录非常重要。

jstack

jstack可以用来查看Java进程里的线程都在干什么,这通常对于应用没反应,非常慢等等场景都有不小的帮助,jstack默认只能看到Java栈,而jstack -m则可以看到线程的Java栈和native栈,但如果Java方法被编译过,则看不到(然而大部分经常访问的Java方法其实都被编译过)。

pstack

pstack可以用来看Java进程的native栈。

perf

一些简单的CPU消耗的问题靠着top -H + jstack通常能解决,复杂的话就需要借助perf这种超级利器了。

cat /proc/interrupts

之所以提这个是因为对于分布式应用而言,频繁的网络访问造成的网络中断处理消耗也是一个关键,而这个时候网卡的多队列以及均衡就非常重要了,所以如果观察到cpu的si指标不低,那么看看interrupts就有必要了。


内存相关工具

碰到一些内存相关的问题时,通常需要用到的工具:

jstat

jstat -gcutil或-gc等等有助于实时看gc的状况,不过我还是比较习惯看gc log。

jmap

在需要dump内存看看内存里都是什么的时候,jmap -dump可以帮助你;在需要强制执行fgc的时候(在CMS GC这种一定会产生碎片化的GC中,总是会找到这样的理由的),jmap -histo:live可以帮助你(显然,不要随便执行)。

gcore

相比jmap -dump,其实我更喜欢gcore,因为感觉就是更快,不过由于某些jdk版本貌似和gcore配合的不是那么好,所以那种时候还是要用jmap -dump的。

mat

有了内存dump后,没有分析工具的话然并卵,mat是个非常赞的工具,好用的没什么可说的。

btrace

少数的问题可以mat后直接看出,而多数会需要再用btrace去动态跟踪,btrace绝对是Java中的超级神器,举个简单例子,如果要你去查下一个运行的Java应用,哪里在创建一个数组大小>1000的ArrayList,你要怎么办呢,在有btrace的情况下,那就是秒秒钟搞定的事,:)

gperf

Java堆内的内存消耗用上面的一些工具基本能搞定,但堆外就悲催了,目前看起来还是只有gperf还算是比较好用的一个,或者从经验上来说Direct ByteBuffer、Deflater/Inflater这些是常见问题。

除了上面的工具外,同样内存信息的记录也非常重要,就如日志一样,所以像GC日志是一定要打开的,确保在出问题后可以翻查GC日志来对照是否GC有问题,所以像-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<gc log file> 这样的参数必须是启动参数的标配。


ClassLoader相关工具

作为Java程序员,不碰到ClassLoader问题那基本是不可能的,在排查此类问题时,最好办的还是-XX:+TraceClassLoading,或者如果知道是什么类的话,我的建议就是把所有会装载的lib目录里的jar用jar -tvf *.jar这样的方式来直接查看冲突的class,再不行的话就要呼唤btrace神器去跟踪Classloader.defineClass之类的了。


其他工具

jinfo

Java有N多的启动参数,N多的默认值,而任何文档都不一定准确,只有用jinfo -flags看到的才靠谱,甚至你还可以看看jinfo -flag,你会发现更好玩的。

dmesg

你的java进程突然不见了? 也许可以试试dmesg先看看。

systemtap

有些问题排查到java层面是不够的,当需要trace更底层的os层面的函数调用的时候,systemtap神器就可以派上用场了。

gdb

更高级的玩家们,拿着core dump可以用gdb来排查更诡异的一些问题。


io类型的问题我排查的很少,所以尽管知道一些工具,还是不在这里写了。


暂时就写这些,欢迎大家秀自己好用的工具,尽管工具的使用多数都可以临时学,但首先知道有哪些工具是最重要的,然后呢还是建议大家可以玩一玩这些工具,这样以后真的要用的时候也不至于一点印象都没有。

<think>嗯,用户之前让我详细介绍了《JVM调优实战:从OOM到GC策略设计》,现在又询问《Java应用性能诊断工具箱》。我需要先理解用户的需求。看起来用户可能是一位Java开发者或者运维人员,正在寻找性能优化方面的资料,特别是工具使用方面的内容。之前的回答结构比较清晰,有章节划分和实际案例,所以用户可能希望类似的详细结构。 首先,我需要确定《Java应用性能诊断工具箱》应该包含哪些内容。用户可能想知道有哪些工具,如何用这些工具进行性能诊断,以及实际案例。结合之前的回答,可能需要包括监控工具、分析工具、调优工具,以及实战案例。 然后,用户可能对工具的具体使用步骤和示例感兴趣,比如如何用Arthas排查问题,或者如何用JProfiler分析内存泄漏。需要确保每个工具都有使用场景、基本用法和案例,这样用户可以直接应用。 另外,用户之前提到了OOM和GC策略,可能对内存和线程相关的诊断工具特别关注。需要涵盖内存分析工具如MAT,线程分析如jstack,以及性能监控如Prometheus。 还要注意工具的分类,比如基础命令、可视化工具、高级诊断工具,这样结构更清晰。实战案例部分需要具体的问题场景,比如CPU飙高或内存泄漏,并展示如何用工具解决。 最后,用户可能希望有一些调优建议和最佳实践,帮助他们避免常见错误,提升效率。需要确保内容实用,步骤详细,代码示例正确,并且符合中文回答的要求,避免使用Markdown格式。</think>以下是为《Java应用性能诊断工具箱》设计的完整技术指南,包含常用工具分类、实战场景和进阶技巧: --- ### **一、性能问题分类与工具选型** #### **1.1 典型性能问题矩阵** | 问题类型 | 关键指标 | 首选工具 | 辅助工具 | |-----------------|----------------------|-------------------------|-------------------------| | CPU飙高 | 用户态CPU>80% | **Arthas**/async-profiler | top/vmstat | | 内存泄漏 | Old Gen持续增长 | **MAT**/jmap | VisualVM/JProfiler | | 线程阻塞 | BLOCKED线程数突增 | **jstack**/Arthas | fastthread.io分析 | | GC频繁 | GC时间占比>20% | **GC日志分析工具** | jstat/gceasy | | 网络瓶颈 | TCP重传率>0.1% | **tcpdump**/Wireshark | netstat/ss | #### **1.2 工具选择黄金法则** - **快速定位阶段**:优先使用CLI工具(jcmd/jstack) - **深度分析阶段**:配合图形化工具(JProfiler/VisualVM) - **生产环境禁忌**:避免直接使用`jmap -dump`(改用`jattach`或Arthas安全dump) --- ### **二、基础诊断工具三剑客** #### **2.1 jcmd(JDK全能王)** ```bash # 获取JVM基本信息(替代jinfo) jcmd <pid> VM.flags # 生成堆转储(安全方式替代jmap) jcmd <pid> GC.heap_dump filename=/path/to/dump.hprof # 列出所有诊断命令 jcmd <pid> help ``` #### **2.2 jstack(线程分析利器)** ```bash # 快速定位死锁(自动检测) jstack -l <pid> | grep -A 10 "deadlock" # 统计线程状态分布 jstack <pid> | awk '/java.lang.Thread.State/ {print $2}' | sort | uniq -c # 持续监控线程创建 jstack <pid> | grep "nid=0x" -o | cut -d "x" -f2 | xargs printf "%d\n" | xargs -I{} echo "obase=16;{}" | bc ``` #### **2.3 jstat(GC监控专家)** ```bash # 动态观察GC状态(每2秒采样) jstat -gcutil <pid> 2000 # 关键指标说明: # S0/S1 - Survivor区利用率 # E - Eden区利用率 # O - 老年代利用率 # FGC - Full GC次数 # FGCT - Full GC总耗时 ``` --- ### **三、高级诊断工具实战** #### **3.1 Arthas(阿里开源的诊断神器)** **场景1:动态方法热修复** ```bash # 1. 反编译问题方法 jad com.example.MyService faultyMethod # 2. 修改代码后编译 mc -c <ClassLoaderHash> /tmp/FixedMyService.java # 3. 热替换类文件 redefine /tmp/FixedMyService.class ``` **场景2:方法级性能分析** ```bash # 监控方法调用耗时(超过100ms时打印参数) watch com.example.OrderService queryOrder '{params, returnObj}' '#cost>100' -x 3 ``` #### **3.2 async-profiler(无侵入式CPU/内存分析)** ```bash # 生成CPU火焰图(采样30秒) ./profiler.sh -d 30 -f /tmp/flamegraph.html <pid> # 内存分配热点分析(追踪对象分配) ./profiler.sh -e alloc -d 60 -f /tmp/alloc.svg <pid> # 结合JFR使用(JDK16+) java -XX:StartFlightRecording:filename=recording.jfr ... ./profiler.sh -d 30 -e jfr -f /tmp/jfr_profile.html <pid> ``` --- ### **四、可视化分析工具链** #### **4.1 Eclipse MAT(内存分析标准工具)** **实战步骤:** 1. 加载堆转储文件(hprof) 2. 查看支配树(Dominator Tree)定位大对象 3. 运行"Leak Suspects"自动分析报告 4. 对比多个堆快照(Histogram对比模式) **高级技巧:** - 使用OQL查询特定对象: ```sql SELECT * FROM java.util.HashMap WHERE size() > 1000 ``` #### **4.2 JProfiler(商业工具标杆)** **核心功能演示:** - **实时内存视图**:对象创建位置跟踪(Allocation Call Tree) - **线程监控**:可视化死锁检测和锁竞争分析 - **JVM健康检查**:自动识别反模式(如String.intern滥用) - **数据库分析**:SQL执行时间与JDBC连接池监控 --- ### **五、生产环境诊断案例库** #### **案例1:CPU 100%问题排查** **现象**:服务器负载飙升,Java进程CPU占用达400%(4核) **排查过程**: 1. **快速定位**: ```bash top -Hp <pid> # 发现线程PID 3456占用90% CPU printf "%x\n" 3456 # 转换为16进制得到0xd80 jstack <pid> | grep -A 20 'nid=0xd80' # 发现循环调用JSON序列化 ``` 2. **源码分析**:定位到反射调用`Method.invoke()`频繁执行 3. **优化方案**:替换为预编译的`Jackson ObjectMapper` #### **案例2:内存泄漏导致频繁Full GC** **现象**:每天凌晨发生OOM,堆内存呈现锯齿状增长 **分析步骤**: 1. **自动Dump配置**: ```bash -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/oom_dump.hprof ``` 2. **MAT分析**:发现自定义缓存类`LocalCache`持有500MB的`ConcurrentHashMap` 3. **根因定位**:缓存未设置失效时间,且使用强引用 4. **解决方案**:改用WeakHashMap + 定时清理线程 --- ### **六、云原生环境诊断进阶** #### **6.1 容器化诊断挑战** - **问题1**:JVM感知不到CGroup内存限制(导致OOM Killer) ```bash # 必须添加JVM参数 -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 ``` - **问题2**:容器内诊断工具缺失 ```dockerfile # Dockerfile最佳实践 RUN apt-get install -y procps telnet lsof tcpdump ``` #### **6.2 Kubernetes诊断三板斧** 1. **检查Pod状态**: ```bash kubectl describe pod <pod-name> | grep -A 10 Events ``` 2. **实时日志追踪**: ```bash stern -n <namespace> <app-label> --since 5m ``` 3. **远程诊断接入**: ```bash kubectl port-forward <pod-name> 3658:3658 # Arthas隧道连接 ``` --- ### **七、性能优化黄金法则** 1. **数据驱动优化**:建立性能基线(如99%响应时间<500ms) 2. **改动验证策略**:遵循"变更一个变量→压测验证→灰度发布"流程 3. **持续监控体系**: ```text Prometheus(指标收集) + Grafana(可视化) + AlertManager(告警) ``` 4. **调优禁忌清单**: - ❌ 盲目增加线程池大小(可能导致上下文切换暴增) - ❌ 过度依赖缓存(可能引发GC问题或数据不一致) - ❌ 未经验证的JVM参数组合(如同时启用多个GC算法) --- 通过这套工具箱的灵活组合使用,开发者可以快速定位从代码缺陷到基础设施层的各类性能问题。建议结合《JVM调优实战》内容,构建完整的性能优化知识体系。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值