Java服务器怎么排查内存?

排查思路总览

1、确认现象:首先确认是否存在内存问题(如响应缓慢、OOM 错误、频繁 Full GC)。

2、监控整体内存使用:使用 JVM 内置工具快速查看内存、GC 状况。

3、生成堆转储文件:在问题发生时,将内存快照保存下来。

4、分析堆转储:使用专业工具分析快照,找到疑似泄漏的对象或大对象。

5、代码定位与修复:根据分析结果,定位到具体代码并进行修复。

第一步:确认现象与监控

通过一些简单的命令来确认问题和获取初步线索。

1. 使用jpsps 找到 Java 进程 ID

jps -l
或
ps -ef | grep java

2. 使用jstat 监控 GC 状况(非常关键)

jstat 可以实时查看 GC 情况和堆内存各区域(Eden, Survivor, Old Gen)的使用率。

每 1 秒打印一次,共打印 10 次 GC 和内存情况
jstat -gcutil <pid> 1s 10

关键指标解读:

O (Old) / MU (Metaspace Utilisation)老年代/元空间使用率,如果持续上升且Full GC后不下降,是内存泄漏的强烈信号。

YGC / YGCTYoung GC 的次数和耗时,频繁的YGC可能意味着 Survivor 区太小或对象过早晋升。

FGC / FGCTFull GC 的次数和耗时,频繁的 Full GC 且耗时很长,会直接导致应用卡顿。

3. 使用jmap 快速查看堆内存概要

显示堆内存的概要信息,包括每个区域的当前用量、容量、使用率
jmap -heap <pid>
显示堆中对象的统计信息,按类型和数量排序(非常有用)
jmap -histo:live <pid> | head -20

-histo 命令可以快速看出哪种类的对象数量最多、占用空间最大,为后续深入分析提供方向。

第二步:生成堆转储文件

当怀疑有内存泄漏或需要深入分析时,必须生成堆转储(Heap Dump)。

1. 使用jmap 命令生成(最常用)

生成堆转储文件,文件会比较大
jmap -dump:live,format=b,file=heap_dump.hprof <pid>

live只 dump 存活的对象,这在生产环境是推荐的,但会触发一次 Full GC。

如果不加live,会 dump 所有对象,包括即将被回收的,文件更大。

2. 在启动参数中配置 OOM 时自动生成(推荐)

在 JVM 启动参数中加入以下配置,这样当发生 OOM 时,会自动生成 dump 文件,便于事后分析。

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/save/dump.hprof

3. 使用图形化工具(如jvisualvm

如果你有图形界面访问权限,可以使用jvisualvm 连接服务器(可能需要配置 JMX),然后手动点击“堆 Dump”按钮。

第三步:分析堆转储文件

生成.hprof 文件后,需要下载到本地,用专业工具分析。

推荐分析工具:

1、Eclipse MAT (Memory Analyzer Tool)最强推荐,功能强大,能自动分析泄漏嫌疑。

2、VisualVM:JDK 自带的工具,功能较基础,但方便快捷。

3、JProfiler:商业软件,功能全面,界面友好,分析效率高。

使用 Eclipse MAT 分析步骤:

1、打开文件:启动 MAT,打开heap_dump.hprof 文件。

2、查看泄漏报告:MAT 通常会提供一个“Leak Suspects Report”(泄漏嫌疑报告),这是它的核心功能,它会自动分析出可能引起内存泄漏的大对象和对象引用链。

3、分析大对象

* 在报告中找到占用内存最大的对象。

* 查看其“Path To GC Roots”(到 GC 根的路径),排除弱引用,这个功能可以显示出是谁在持有这些大对象的引用,导致它们无法被垃圾回收。

4、分析支配树:通过“Dominator Tree”(支配树)视图,可以清晰地看到整个内存中的对象层级关系,找出哪些对象持有了最多的内存。

5、定位代码:根据分析出的可疑类和引用链,回溯到你的项目代码中,找到创建这些对象的地方。

第四步:排查非堆内存(Off-Heap)问题

内存问题不一定都在堆上,如果堆内存正常,但物理内存持续增长,可能是非堆内存问题。

元空间泄漏

Java 8 之后 PermGen 被 Metaspace 取代,如果加载的类过多(如动态代理、OSGi 环境),可能导致元空间膨胀。

监控使用jstat -gcutil <pid>MU 指标。

排查检查是否有类加载器泄漏(如 Web 应用重启后旧类加载器未被回收)。

直接内存泄漏

通过ByteBuffer.allocateDirect() 申请的内存不在堆上,不受 GC 直接管理。

监控NIO 和 Netty 应用需特别注意,JVM 参数-XX:MaxDirectMemorySize 可以限制大小。

排查工具对其可见性差,可以尝试在启动参数中加入-XX:NativeMemoryTracking=summary,然后使用jcmd <pid> VM.native_memory summary 来跟踪 native memory 的使用情况。

第五步:实战排查清单

1、收到告警/发现问题:应用变慢、OOM 错误日志。

2、立刻执行jps ->jstat -gcutil <pid> 1s,观察 Old 区和 Metaspace 使用率趋势。

3、立刻执行jmap -histo:live <pid> | head -20,看哪个对象最多、最大。

4、决定是否 dump:如果内存使用率持续攀升且不回落,立即用jmap -dump 生成堆转储。(如果条件允许)

5、分析 dump:将文件下载到本地,用 MAT 打开,遵循“Leak Suspects” -> “Dominator Tree” -> “Path to GC Roots” 的流程。

6、结合代码:根据 MAT 给出的可疑类,去代码中查找创建该实例的地方,检查集合类(如 static Map)、缓存、监听器、连接等是否未正确释放。

7、修复与验证:修复代码后,在测试环境模拟类似场景,用工具观察内存是否恢复稳定。

常用 JVM 参数(用于预防和排查)

基本内存设置
-Xms4g -Xmx4g           # 堆最小和最大设为相同,避免运行时扩容收缩带来的性能波动
-XX:NewRatio=2          # 老年代/新生代的比例,默认就是2
-XX:SurvivorRatio=8     # Eden/Survivor区的比例
输出GC日志(极其重要!)
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:/path/to/gc.log # GC日志输出路径
OOM时自动dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/heap-dumps/
跟踪类加载等情况
-XX:+TraceClassLoading
-XX:+TraceClassUnloading

希望这个详细的指南能帮助你有效地排查 Java 服务器内存问题!工具只是手段,最关键的是理解 JVM 内存模型和 GC 原理,才能对工具输出的数据做出正确的解读。

文章摘自:https://idc.huochengrm.cn/fwq/15281.html

评论