Java内存泄漏、CPU飙升排查

在Java应用开发中,内存泄漏和CPU飙升是两类高频出现的生产问题,也是常见的面试问题。这里通过一些demo进行实践。

内存泄漏

java 复制代码
private static List<byte[]> leakList = new ArrayList<>();

@GetMapping("/memory/leak")
public void test2() {
    try {
        while (true) {
            // 分配1MB的内存块
            byte[] block = new byte[1024 * 1024];
            leakList.add(block);

            // 每隔100毫秒分配一次,模拟内存不断积累
            Thread.sleep(100);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
shell 复制代码
java -jar -Xms256m -Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/wx/workspace/app.hprof test.jar


jstat -gcutil <pid> 1000  # 观察Old区使用率持续上涨

-XX:+HeapDumpOnOutOfMemoryError: 在发生 OutOfMemoryError 时自动生成堆转储文件(Heap Dump) -XX:HeapDumpPath: 指定堆转储文件的保存位置,可以使用工具(如 MAT、JProfiler 等)打开和分析

可以通过jmap命令生成hprof报告

shell 复制代码
jmap -dump:live,format=b,file=heap_dump.bin <pid>

# 查看Java进程
jps -l
jcmd
ps -ef | grep java

使用JProfiler打开*.hprof文件,查看最大内存占有的数据类型

点击目标数据类型,选择incoming references

  • incoming references:查看目标数据类型被哪些对象引用了
  • outgoing references:查看目标数据类型引用了哪些对象

找到泄漏对象 LeakedObject由于被静态集合引用而无法被垃圾回收,从而确定位置

CPU飙升

java 复制代码
@GetMapping("/cpu")
public void test3() {
    CompletableFuture<Void> t1 = CompletableFuture.supplyAsync(() -> {
            while (true) {
                // 密集计算,无退出条件
                double result = 0;
                for (int i = 0; i < 100000; i++) {
                    result += Math.sqrt(i) * Math.tan(i);
                }
            }
        }, threadPoolExecutor);

        CompletableFuture<Void> t2 = CompletableFuture.supplyAsync(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    System.out.println("正常业务运行中...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, threadPoolExecutor);
        CompletableFuture.allOf(t1, t2).join();
}
  1. 定位Java进程,top -c
  2. 查看线程级别CPU占用,top -Hp <PID>
  3. 抓去线程快照,jstack <PID> > thread.txt
  4. 线程ID转换printf "%x\n" <线程ID>
  5. 进行分析
txt 复制代码
"WXW-Thread-1" #35 daemon prio=5 os_prio=31 cpu=150071.37ms elapsed=150.75s tid=0x00007fdfb58fea00 nid=0x7207 runnable  [0x0000700006795000]
   java.lang.Thread.State: RUNNABLE
	at com.example.controller.TestThreadController.lambda$test3$2(TestThreadController.java:94)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(java.base@17.0.13/CompletableFuture.java:1768)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@17.0.13/ThreadPoolExecutor.java:1136)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@17.0.13/ThreadPoolExecutor.java:635)
	at java.lang.Thread.run(java.base@17.0.13/Thread.java:840)

通过日志查找问题所在

使用JProfiler分析CPU视图,定位异常线程和代码

内存结构

JVM(Java虚拟机)的内存结构是其运行时数据区域的核心组成部分,用于管理程序执行过程中的内存分配和回收

程序计数器

作用

记录当前线程执行的位置(字节码指令地址)

特性
  1. 线程私有,每个线程独立存储
  2. 唯一不会发生 OutOfMemoryError 的区域

虚拟机栈

作用

存储方法调用栈帧(每个方法对应一个栈帧),管理局部变量、操作数栈、动态链接和方法返回地址

栈帧结构
  • 局部变量表:存放方法参数和方法内定义的局部变量(基本类型和对象引用)
  • 操作数栈:执行自己饿吗指令的临时操作数存储区
  • 动态链接:指向运行时常量池中该方法的符号引用
  • 返回地址:方法退出后需要返回的位置
特点
  1. 线程私有
  2. 可能抛出的异常
    • StackOverflowError:栈深度超过限制(无限递归)
    • OutOfMemoryError:扩展栈是无法申请足够内存

本地方法栈

作用

为Native方法提供服务

特性

线程私有

方法区

作用

存储类信息、常量、静态变量、即时编译器编译后的代码,包含运行时常量池,存放编译期生成的字面量、符号引用及运行时添加的常量

特性
  1. 线程共享
  2. 可能抛出OutOfMemoryError

堆是Java应用程序最重要的一个内存结构

作用

存放所有对象实例和数组(通过 new 关键字创建的对象)

特性
  1. 线程共享
  2. 可能抛出OutOfMemoryError
  3. 通过 -Xmx-Xms 设置最大和初始堆大小,最好是设置一致
堆内存划分
  1. 新生代
    • Eden区:对象初次分配的区域
    • Survivor区:存放经过Minor GC后存活的对象
  2. 老年代:存放长期存活的对象(经过多次GC未被回收)
  3. 老年代:新生代=2:1,Eden区:Survivor0区:Survivor1区=8:1:1
相关推荐
wang09076 分钟前
自己动手写一个spring之IOC_2
java·后端·spring
ltl40 分钟前
推理退化:为什么大模型会输出乱码、死循环和无意义文本
后端
ltl1 小时前
架构视图与文档:C4 模型从入门到实战
后端
IT_陈寒4 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端
无风听海4 小时前
多租户系统中的 OIDC:Discovery 端点与联合登录的深度实践
后端·python·flask
小小前端仔LC4 小时前
Node.js + LangChain + React:搭建个人知识库(六)- “吃什么”项目实战:从700+菜谱入库到Taro H5端JSON渲染
前端·后端
程序员黑豆5 小时前
AI全栈开发之Java:怎么配置Java环境变量
前端·后端·ai编程
苍何5 小时前
一手实测 Claude Fable 5,手搓了个 Obsidian 的 Codex 插件
后端
swipe6 小时前
做多轮对话 Agent,为什么我建议把短期记忆放到 Redis
后端·面试·llm
程序员黑豆6 小时前
AI全栈开发之Java:什么是JDK
前端·后端·ai编程