手把手教你排查CPU100%和JVM内存溢出问题

  1. 内存溢出(Memory Overflow)指的是程序在申请内存时,向操作系统申请了一块内存空间,但由于某种原因(如程序错误、缺乏足够的内存等),导致程序使用的内存超过了申请到的内存大小。这会导致程序崩溃或者出现未定义的行为。

  2. 内存泄漏(Memory Leak)指的是程序在动态分配内存后,没有及时释放已经不需要的内存空间,导致系统中存在大量无用的内存占用,最终可能会导致系统的性能下降或者耗尽可用内存资源。

可以这样理解:内存溢出是指程序需要的内存超过了可用内存;而内存泄漏则是指程序本身的内存管理不当,使用了系统提供的内存却没有及时释放。

内存100%案例

「堆内存溢出代码示例:」

private static List<byte[]> CACHE = new ArrayList<>(1000);

@GetMapping("/tuningTest")
public String tuningTest(){
    for (int loopTimes = 1; loopTimes < 20; loopTimes++) {
        long cacheSize = ObjectSizeCalculator.getObjectSize(CACHE);
        LOGGER.info("第 {} 次循环前, 对象已占用 {} M 内存", loopTimes, cacheSize / 1024 / 1024);

        // 每次占用5M内存
        CACHE.add(new byte[5 * 1024 * 1024]);


    }
    return "OK";
}

启动参数:java -jar -Xms32m -Xmx64m -XX:PermSize=15M -XX:MaxPermSize=30M jvmtest-0.0.1-SNAPSHOT.jar

测试时为了效果明显可以用压测工具,并发数20,轮次1000。更有助于分析问题

top 命令查看cpu和内存情况

图片

top 命令查看cpu和内存情况

jmap -heap PID查看jvm内存使用情况

图片

jmap -heap PID查看jvm内存使用情况

从图中可以看出老年区的内存使用率已经达到了96%

jstat -gc PID 查看GC的情况

  • YGC : YG GC的次数

  • YGCT:YG GC的平均时间

  • FGC:FULL GC的次数

  • FGCT:FULL GC的平均时间

图片

jstat -gc PID 查看GC的情况

这里可以连续看几次就会发现FULL GC的次数在疯狂增长,而且FULL GC的平均时间也会增长,这就是CPU100%的原因,因为CPU一直在尝试垃圾回收

jmap

jmap -dump:format=b,file=./jmap_dump.hprof PID使用jmap命令生成分析所需要用的dump文件。

使用Jprofiler打开(为什么不用mat,因为我没安装上,不知道为啥)

图片

Jprofiler

图片

Jprofiler

如上图已经定位出来了问题,这个工具还是很好用的,有兴趣的大佬们可以研究研究

CPU100%案例

「代码示例」

/**
 * 死循环
 *
 * @return
 */
@RequestMapping("/deadCycle")
public String deadCycle() {
    for (int i = 1; i > 0; i++) {
        System.out.println(i);
    }
    return "Success";
}

/**
 * 死锁
 *
 * @return
 */
@RequestMapping("/deadLock")
public String deadLock() {
    Lock lock1 = new ReentrantLock();
    //线程t1中
    new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (!lock1.tryLock()) {
            System.out.println("线程1 一直自旋获取锁");
        }
        System.out.println("线程1 获取到锁 do something");
    }).start();

    // 线程2
    new Thread(() -> {
        lock1.lock();  //此时线程2 获取到锁 但是一直阻塞 导致锁无法释放
        while (true) {

        }
    }).start();

    return "Success";
}

这里就整一个最简单里例子,目的就是为了让CPU一直在运算。

top 命令查看CPU占用100%的进程号

图片

top 命令查看CPU占用100%的进程号

top命令

top -H -n 1 -p PID然后通过命令查找对应进程下线程的状态

图片

top -H -n 1 -p PID

jstack

然后通过jstack -l PID > ./jstack.log命令输出进程的线程文件

jstack.log的文件PID是十六机制,从3.2步骤中我们可以看出PID为25778的线程CPU特别高,25778二机制为0x64b2。拿到16进制去文件里搜索能够很明确看出哪里出现了问题

图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值