半滴蜜糖 發表於 2026-1-11 11:44:22

java线上问题排查之内存占用大解决步骤

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、快速排查流程</li><li>二、详细排查步骤与命令</li><ul class="second_class_ul"><li>步骤1:快速定位问题进程</li><li>步骤2:分析JVM内存分布</li><li>步骤3:生成和分析堆转储</li><li>步骤4:分析堆转储文件</li><ul class="third_class_ul"><li>使用Eclipse MAT分析</li><li>使用命令行快速分析</li></ul><li>步骤5:检查非堆内存</li><ul class="third_class_ul"></ul></ul><li>三、具体场景排查命令</li><ul class="second_class_ul"><li>场景1:堆内存泄漏排查</li><ul class="third_class_ul"></ul><li>场景2:元空间溢出排查</li><ul class="third_class_ul"></ul><li>场景3:线程内存泄漏排查</li><ul class="third_class_ul"></ul><li>场景4:直接内存泄漏排查</li><ul class="third_class_ul"></ul></ul><li>四、使用Arthas进行在线诊断</li><ul class="second_class_ul"></ul><li>五、容器环境特殊排查</li><ul class="second_class_ul"></ul><li>六、自动化监控脚本</li><ul class="second_class_ul"><li>内存监控脚本</li><ul class="third_class_ul"></ul><li>快速排查一键脚本</li><ul class="third_class_ul"></ul></ul><li>七、常见问题模式与解决方案</li><ul class="second_class_ul"><li>模式1:缓存无限增长</li><ul class="third_class_ul"></ul><li>模式2:资源未关闭</li><ul class="third_class_ul"></ul><li>模式3:类加载器泄漏</li><ul class="third_class_ul"></ul><li>模式4:大对象分配</li><ul class="third_class_ul"></ul><li>根据症状快速定位问题</li><ul class="third_class_ul"></ul></ul><li>八、预防措施</li><ul class="second_class_ul"></ul><li>总结&nbsp;</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>一、快速排查流程</h2>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026111114300592.png" /></p>
<p class="maodian"></p><h2>二、详细排查步骤与命令</h2>
<p class="maodian"></p><h3>步骤1:快速定位问题进程</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 查看系统整体内存使用
free -h
# 查看系统整体内存
free -h &amp;&amp; echo "---" &amp;&amp; top -bn1 | head -10
top -p &lt;java_pid&gt;

# 2. 查找Java进程内存排名
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -20
# 定位Java进程内存排名
ps -eo pid,ppid,cmd,%mem,rss --sort=-rss | head -15
# 查看Java进程列表
jps -mlv | grep -v Jps
# 或者使用更专业的工具
nmon -s1 -c10

# 3. 确认Java进程基本信息
jps -mlv
jcmd -l
</pre></div>
<p class="maodian"></p><h3>步骤2:分析JVM内存分布</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 查看堆内存详细分布
jhsdb jmap --heap --pid &lt;pid&gt;
jhsdb jmap --heap --pid $PID 2&gt;/dev/null || jmap -heap $PID
# 2. 实时监控GC和内存使用
jstat -gcutil &lt;pid&gt; 1000 10
jstat -gccapacity &lt;pid&gt; 1000 5

# 3. 查看内存各区域使用详情
jcmd &lt;pid&gt; VM.native_memory summary scale=MB
jcmd $PID VM.native_memory summary scale=MB

# 检查大对象分布
jmap -histo:live $PID | sort -k3 -nr | head -30 &gt; large_objects.txt
</pre></div>
<p><strong>关键指标解读:</strong></p>
<ul><li><code>OU</code>(Old Utilization):老年代使用率 &gt;80% 需关注</li><li><code>MU</code>(Metaspace Utilization):元空间使用率</li><li><code>CCS</code>(Compressed Class Space):压缩类空间</li></ul>
<p class="maodian"></p><h3>步骤3:生成和分析堆转储</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 生成堆转储文件(生产环境谨慎使用)
jmap -dump:live,format=b,file=heap_dump.hprof &lt;pid&gt;

# 2. 如果jmap不可用,使用jcmd(推荐)
jcmd &lt;pid&gt; GC.heap_dump filename=heap_dump.hprof
jcmd $PID GC.heap_dump filename=heap_$(date +%Y%m%d_%H%M%S).hprof

# 3. 仅统计对象数量(不影响服务)
jmap -histo:live &lt;pid&gt; | head -50 &gt; object_histo.txt
# 仅生成直方图(生产环境安全)
jmap -histo $PID &gt; object_histogram.tx
</pre></div>
<p class="maodian"></p><h3>步骤4:分析堆转储文件</h3>
<p class="maodian"></p><h4>使用Eclipse MAT分析</h4>
<div class="jb51code"><pre class="brush:ps;"># 下载MAT:https://www.eclipse.org/mat/
# 分析步骤:
1. File → Open Heap Dump
2. 查看Leak Suspects Report
3. 分析Dominator Tree
4. 查看重复字符串/集合大小
</pre></div>
<p class="maodian"></p><h4>使用命令行快速分析</h4>
<div class="jb51code"><pre class="brush:ps;"># 分析大对象
jhat heap_dump.hprof
# 访问 http://localhost:7000 查看分析结果

# 或者使用jHiccup
java -jar jHiccup.jar -p &lt;pid&gt;
</pre></div>
<p class="maodian"></p><h3>步骤5:检查非堆内存</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 检查元空间使用
jstat -gcmetacapacity &lt;pid&gt;

# 2. 检查线程数量
ps -eLf | grep &lt;pid&gt; | wc -l
jstack &lt;pid&gt; | grep "java.lang.Thread.State" | wc -l
jstack $PID | grep "java.lang.Thread.State" | sort | uniq -c &gt; thread_states.txt

# 3. 检查直接内存
jcmd &lt;pid&gt; VM.native_memory detail | grep -A10 "Internal"
# 类加载统计
jcmd $PID VM.classloader_stats 2&gt;/dev/null

# 4. 检查代码缓存
jstat -gccapacity &lt;pid&gt; | grep -i code
</pre></div>
<p class="maodian"></p><h2>三、具体场景排查命令</h2>
<p class="maodian"></p><h3>场景1:堆内存泄漏排查</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 实时监控对象创建
jstat -gcutil &lt;pid&gt; 1s

# 2. 跟踪GC日志(如果已开启)
tail -f /path/to/gc.log
# 实时查看GC情况
tail -f /path/to/gc.log | grep -E "Full GC|GC.*Pause"
# 使用分析工具(需要下载gcviewer)
java -jar gcviewer.jar /path/to/gc.log

# 3. 检查大对象
jmap -histo &lt;pid&gt; | sort -k3 -nr | head -20

# 4. 检查Finalizer队列
jcmd &lt;pid&gt; GC.finalizer_info
</pre></div>
<p class="maodian"></p><h3>场景2:元空间溢出排查</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 监控元空间增长
while true; do jstat -gcmetacapacity &lt;pid&gt;; sleep 2; done

# 2. 检查类加载器
jcmd &lt;pid&gt; VM.classloader_stats

# 3. 检查加载的类数量
jmap -clstats &lt;pid&gt;

# 4. 查找重复类
java -jar jarpath/classloader-analyzer.jar --pid &lt;pid&gt;
</pre></div>
<p class="maodian"></p><h3>场景3:线程内存泄漏排查</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 生成线程转储
jstack &lt;pid&gt; &gt; thread_dump.txt

# 2. 统计线程状态
grep "java.lang.Thread.State" thread_dump.txt | sort | uniq -c

# 3. 检查线程栈大小
jinfo &lt;pid&gt; | grep -i stack

# 4. 分析线程创建轨迹(如果有Arthas)
thread -n 10
</pre></div>
<p class="maodian"></p><h3>场景4:直接内存泄漏排查</h3>
<div class="jb51code"><pre class="brush:ps;"># 1. 检查NIO直接内存
jcmd &lt;pid&gt; VM.native_memory summary | grep -A5 "Native Memory Tracking"

# 2. 监控堆外内存使用
pmap -x &lt;pid&gt; | sort -k3 -nr | head -10

# 3. 如果怀疑Netty等框架的内存泄漏
# 添加JVM参数:-XX:MaxDirectMemorySize=512m
</pre></div>
<p class="maodian"></p><h2>四、使用Arthas进行在线诊断</h2>
<div class="jb51code"><pre class="brush:ps;"># 安装并启动Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 内存诊断命令
dashboard                  # 整体监控面板
memory                     # 内存详情
heapdump --live /tmp/heap.hprof# 在线堆转储

# 类加载监控
classloader -t            # 类加载器树
sc -d &lt;ClassName&gt;         # 查看类详情

# 方法级监控
monitor -c 5 com.example.Service methodName# 方法执行统计
watch com.example.Service methodName "{params,returnObj}" -x 3# 观察方法参数
</pre></div>
<p class="maodian"></p><h2>五、容器环境特殊排查</h2>
<div class="jb51code"><pre class="brush:ps;"># 1. 进入容器
docker exec -it &lt;container_id&gt; /bin/bash

# 2. 检查容器内存限制
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
cat /sys/fs/cgroup/memory/memory.usage_in_bytes

# 3. 调整JVM内存参数(如果设置不合理)
# 在启动参数中添加:
-XX:+UseContainerSupport
-XX:MaxRAMPercentage=75.0

# 4. 检查OOM Killer日志
dmesg | grep -i "killed process"
grep -i "oom" /var/log/messages
</pre></div>
<p class="maodian"></p><h2>六、自动化监控脚本</h2>
<p class="maodian"></p><h3>内存监控脚本</h3>
<div class="jb51code"><pre class="brush:ps;">#!/bin/bash
PID=$1
LOG_FILE="memory_monitor_${PID}.log"

while true; do
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    # 收集内存指标
    HEAP_INFO=$(jstat -gcutil $PID 2&gt;/dev/null | tail -1)
    THREAD_COUNT=$(ps -eLf | grep $PID | wc -l)
   
    echo "$TIMESTAMP | $HEAP_INFO | Threads: $THREAD_COUNT" &gt;&gt; $LOG_FILE
    sleep 30
done
</pre></div>
<p class="maodian"></p><h3>快速排查一键脚本</h3>
<div class="jb51code"><pre class="brush:ps;">#!/bin/bash
PID=$1
echo "=== 内存问题快速排查 ==="
echo "1. 进程信息:"
jps -mlv | grep $PID

echo -e "\n2. 内存分布:"
jstat -gcutil $PID

echo -e "\n3. 对象统计:"
jmap -histo $PID | head -30

echo -e "\n4. 线程统计:"
jstack $PID | grep "java.lang.Thread.State" | sort | uniq -c
</pre></div>
<p class="maodian"></p><h2>七、常见问题模式与解决方案</h2>
<p class="maodian"></p><h3>模式1:缓存无限增长</h3>
<p><strong>症状:</strong> Old区持续增长,Full GC后很快又满<br /><strong>解决:</strong></p>
<ul><li>检查缓存失效策略</li><li>添加缓存大小限制</li><li>使用WeakReference/SoftReference</li></ul>
<p class="maodian"></p><h3>模式2:资源未关闭</h3>
<p><strong>症状:</strong> 直接内存或文件描述符泄漏<br /><strong>解决:</strong></p>
<ul><li>使用try-with-resources</li><li>检查数据库连接池配置</li><li>监控文件描述符数量</li></ul>
<p class="maodian"></p><h3>模式3:类加载器泄漏</h3>
<p><strong>症状:</strong> 元空间持续增长,频繁Full GC<br /><strong>解决:</strong></p>
<ul><li>检查热部署/动态类生成</li><li>分析类加载器引用链</li><li>重启应用服务</li></ul>
<p class="maodian"></p><h3>模式4:大对象分配</h3>
<p><strong>症状:</strong> 年轻代GC频繁,对象直接进入老年代<br /><strong>解决:</strong></p>
<ul><li>调整-XX:PretenureSizeThreshold</li><li>优化大对象使用模式</li><li>增加年轻代大小</li></ul>
<p class="maodian"></p><h3>根据症状快速定位问题</h3>
<table><thead><tr><th>症状</th><th>可能原因</th><th>排查命令</th></tr></thead><tbody><tr><td><strong>Old区持续增长</strong></td><td>内存泄漏、缓存问题</td><td><code>jmap -histo:live</code>, MAT分析</td></tr><tr><td><strong>频繁Full GC</strong></td><td>内存不足、元空间满</td><td><code>jstat -gcutil</code>, 检查Metaspace</td></tr><tr><td><strong>Young GC频繁</strong></td><td>新生代过小、短命对象多</td><td>调整-XX:NewSize, 检查代码</td></tr><tr><td><strong>元空间增长</strong></td><td>类加载器泄漏、动态类生成</td><td><code>jcmd VM.classloader_stats</code></td></tr><tr><td><strong>线程数暴涨</strong></td><td>线程池配置问题、阻塞</td><td><code>jstack</code>, 线程dump分析</td></tr><tr><td><strong>堆外内存高</strong></td><td>NIO DirectBuffer、JNI</td><td><code>jcmd VM.native_memory</code></td></tr></tbody></table>
<p class="maodian"></p><h2>八、预防措施</h2>
<div class="jb51code"><pre class="brush:ps;"># 1. 添加必要的JVM参数
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps
-XX:ErrorFile=/path/to/hs_err_pid%p.log
-Xlog:gc*,gc+age=debug:file=/path/to/gc.log

# 2. 设置合理的内存限制
-XX:MaxMetaspaceSize=512m
-XX:ReservedCodeCacheSize=256m
-Xss256k

# 3. 监控告警配置
# 关键指标:堆使用率 &gt;80%,Full GC频率,元空间增长
</pre></div>
<p class="maodian"></p><h2>总结&nbsp;</h2>
<p>到此这篇关于java线上问题排查之内存占用大解决步骤的文章就介绍到这了,更多相关java内存占用大内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>java应用占用内存过高排查的解决方案</li><li>java如何获取系统CPU、内存占用</li><li>java排查进程占用系统内存高方法</li><li>如何查看java进程内存占用情况</li><li>java内存占用高案例</li><li>Java如何使用命令查看内存占用情况</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: java线上问题排查之内存占用大解决步骤