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([email protected]/CompletableFuture.java:1768)
	at java.util.concurrent.ThreadPoolExecutor.runWorker([email protected]/ThreadPoolExecutor.java:1136)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run([email protected]/ThreadPoolExecutor.java:635)
	at java.lang.Thread.run([email protected]/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
相关推荐
EngineerSuTao9 分钟前
Flask的app.run()里发生了什么
后端·python·flask
Asthenia041210 分钟前
进程与线程区别 / Java线程状态 / 同步方法与代码块差异 / Monitor线程同步原理 / 死锁解析 / 多线程访问资源避免死锁
后端
Asthenia041229 分钟前
模块引用/ pass/ 模块包/ range/xrange/ dict/items/ is/ func引用/ any/all/ 列表值/ 排序
后端
计算机学姐33 分钟前
基于SpringBoot的名著阅读网站
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
努力的小雨35 分钟前
Spring AI 增加混元 embedding 向量功能
后端
慕容靖翾39 分钟前
CSS语言的排序算法
开发语言·后端·golang
Asthenia04121 小时前
装饰器 / Lambda / Tuple-List / except / match-search / 全局变量 / 引号 / 参数 / 类-实例变量
后端
阳光照进我心窝1 小时前
Laravel 模型静态方法实现
后端
喵个咪1 小时前
Ent代码生成工具链
后端·go
SimonKing1 小时前
XXL-JOB:揭秘定时机制
java·后端·架构