跳至主要內容

JVM调优实战场景- 内存溢出和死锁

科哒大约 4 分钟

内存溢出的定位与分析

模拟内存溢出

package leetcode.editor.cn.doc;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 *
 * </p>
 *
 * @author huangKeMing
 * @since 2025/4/28
 */
public class TestJvmOutOfMemory {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            String str = "";
            for (int j = 0; j < 1000; j++) {
                str += UUID.randomUUID().toString();
            }
            list.add(str);
        }
        System.out.println("ok");
    }
}

运行测试

MAT插件下载

Memory Analyzer (MAT) | The Eclipse Foundationopen in new window

下载后解压运行 MemoryAnalyzer.exe

使用MAT分析报告

根据报告分析,数组对象Oject[] 占用的80%的内存信息,还提示出现代码问题的地方。

查看 Details 详情

可以看到集合中存储的数据,存储了大量的UUID字符串。

************

package com.ke.middleware.test;

/**
 * <p>
 *
 * </p>
 *
 * @author huangKeMing
 * @since 2025/4/28
 */
public class TestDeadLock {
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    public static void main(String[] args) {
        new Thread(new Thread1()).start();//启动线程01
        new Thread(new Thread2()).start();//启动线程02
    }
    //线程01
    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            synchronized (obj1) {
                System.out.println("Thread1 拿到了 obj1 的锁!");
                try {
// 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();}
                synchronized (obj2) {
                    System.out.println("Thread1 拿到了 obj2 的锁!");
                }
            }
        }
    }
    //线程02
    private static class Thread2 implements Runnable {
        @Override
        public void run() {
            synchronized (obj2) {
                System.out.println("Thread2 拿到了 obj2 的锁!");
                try {
// 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj1) {
                    System.out.println("Thread2 拿到了 obj1 的锁!");
                }
            }
        }
    }
}

在win下查看死锁

## 查看所有java进程
tasklist | findstr java
## 查看某一java进程的堆栈信息
jstack 15616  

在Linux系统下

程序启动运行之后,使用jps查找java进行。 jstack打印堆栈信息

jstack 18487 | grep 'BLOCKED' -A 15 --color 1

可以清楚的看到,两个线程相互等待持有的锁。是造成死锁的原因

使用Arthas进行分析

thread -b

可以精确定位到出现死锁问题的代码。相比使用命令操作省去了很多步骤,直接可以打印线程信息。