LHon(学习笔记)——分析OOM产生原因

本文介绍了Java内存模型JVM,重点分析了堆内存溢出(OOM)的原因,并提供了使用jmap和jstack等工具进行故障排查的方法。通过模拟堆内存溢出,展示了如何生成Heap Dump文件,以及利用jvisualvm分析内存使用情况和查找问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JVM

在弄清楚OOM产生的原因之前,我们务必要搞清楚Java的内存模型,也就是JVM,这可以参考我之前的一个学习笔记 JVM学习笔记

先来看一下要用到的JVM的一些参数:

参数名称含义
-Xms初始堆大小
-Xmx最大堆大小
-Xmn年轻代大小
-Xss每个线程的堆栈大小
-XX:+HeapDumpOnOutOfMemoryError目录下生成堆的Dump文件

当初始化堆内存容量小于MinHeapFreeRatio 时,JVM会增大堆直到Xmx最大限制,当空余内存大于MaxHeapFreeRatio时,JVM会减小堆直到Xms最小限制,其中MinHeapFreeRatio和MaxHeapFreeRatio都是可以配置的。

OOM

OOM全称Out Of Memory,被称为是内存溢出(OutOfMemoryError),在JVM运行时的内存区域里,除了程序计数器,其它几个区域都可能会发生内存溢出的异常,下面主要来分析堆内存上面的OOM。

堆内存的OOM

接下来模拟一个堆上发生的OOM,编写Java代码,其思路就是往List集合里面无限放匿名对象:

package com.mezjh.test;

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

/**
 *
 * @author ZJH
 * @date 2020/8/12 14:48
 */
public class TestHeapOOM {

    public static void main(String[] args) {
        List<TestObject> list = new ArrayList<>();
        while (true) {
            list.add(new TestObject());
        }
    }
}

其中TestObject是一个什么都没有的类:

package com.mezjh.test;

/**
 * @author ZJH
 * @date 2020/8/12 15:25
 */
public class TestObject {
}

接下来将最大的堆内存设置为100m,然后运行代码,会很快发生OOM异常
在这里插入图片描述

出现的异常如下,即OutOfMemoryError:
在这里插入图片描述
我们分析OutOfMemoryError异常时一般需要借助一些工具,以便于更快定位问题所在。

Heap Dump

它是Java进程所使用的内存情况在某一时间的一次快照,以文件的形式持久化到磁盘中。其一般包括如下信息:

  • 所有对象信息
  • 所有的类信息
  • 垃圾回收的根对象
  • 线程栈及局部变量
    接下来我们使用JVM的配置参数生成Dump文件,修改JVM配置,如下图所示,这样会在发生OutOfMemoryError异常时生成堆的Dump文件:
    在这里插入图片描述
    在异常信息上方可以看到生成了该文件
    在这里插入图片描述
    在项目文件目录下,可以找到刚才生成的Dump文件:
    在这里插入图片描述
    直接打开的话,里面全部都是二进制,这里需要使用如下图所示的jdk自带的可视化工具jvisualvm.exe(Java性能分析工具)打开该文件。
    在这里插入图片描述
    打开之后可以看到一些基本信息:
    在这里插入图片描述
    点进类选项卡,这里可以很清楚的看到TestObject对象几乎占了所有内存,实例数占了99.7%。
    在这里插入图片描述
    同时jvisualvm也可以分析运行中的Java的内存的使用情况,接下来可以看看如何分析在运行时候的内存使用情况,先打开该工具,为了分析Java的内存使用情况,需要安装以下插件
    在这里插入图片描述
    然后启动一个Java线程,如下图所示,左侧栏会显示该线程,我们打开此线程:
    在这里插入图片描述
    选中Visual GC,会清楚的看到在何时发生了GC(JVM学习笔记中有提到堆内存的GC过程):
    在这里插入图片描述
    生成Dump文件的方式除了上述修改JVM参数之外,还可以通过命令行的方式来生成该文件:
  • jmap(Memory Map for Java):生成虚拟机的内存转储快照(heapdump文件);
  • jstack(Stack Trace for Java):显示虚拟机的线程快照;
jmap

是一个多功能命令,它可以生成Java程序的dump文件,也可以查看堆内对象信息,查看ClassLoader的信息以及finalizer队列。
要使用jmap,需要用jps查看当前Java程序的进程ID:

在这里插入图片描述
jmap [pid] 查看当前共享对象的信息,从左到右一次为起始地址,大小,路径
在这里插入图片描述
jmap -heap [pid]
这个命令可以看到堆的配置信息,可以看到这是我们设置的最大堆大小MaxHeapSize为100M
在这里插入图片描述
除此之外,还可以看到堆中各个区域的内存使用情况
在这里插入图片描述
jmap -histo:live [pid]
这个命令可以查看类的使用情况,如下图,TestObject对象一共有98970个,其占用内存是1583520 byte。
在这里插入图片描述
jmap -clstats [pid]
该命令可以查看类加载器的信息,如下图,根类加载器加载了1475个类…
在这里插入图片描述
jmap -finalizerinfo [pid]
查看finalizer队列,即要被执行垃圾回收的队列,此时 Number of objects pending for finalization为0,就代表要被回收的对象为0个。
在这里插入图片描述
jmap -dump:live,format=b,file=jmap.bin [pid]
这个命令可以打印Dump文件,live代表存活的对象,b代表以2进制的形式,file代表目录,
在这里插入图片描述
在这里插入图片描述

jstack

查看Java应用程序中线程堆栈信息,我们的程序在运行时卡顿或长时间未响应时可以使用该命令。

-F 当线程挂起时,使用jstack -l [pid] 不被响应时,强制输出线程堆栈;
-l 除堆栈外,显示关于锁的附加信息;
jstack -F [pid]
查看堆栈信息,No deadlocks found ,说明没有死锁发生,state代表线程的状态
在这里插入图片描述
接下来编写一个死循环代码:用jstack查看堆栈信息

package com.mezjh.test;

/**
 *
 * @author ZJH
 * @date 2020/8/12 14:48
 */
public class TestHeapOOM {

    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    public static void test1() {
        while(true) {

        }
    }
}

如果cpu占用过高导致卡顿,堆栈并没有溢出,此时jstack这个命令就有用了,它能排查出绝大多数死循环或者死锁问题。
在这里插入图片描述
接下来用jstack检查一下死锁:
新增方法2,代码如下,在方法2中制造了一个死锁场景:

public static void test2() {
        Lock lock1 = new ReentrantLock();
        Lock lock2 = new ReentrantLock();
        new Thread(() -> {
            try {
                lock1.lock();
                Thread.sleep(300);
                lock2.lock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread1").start();
        new Thread(() -> {
            try {
                lock2.lock();
                Thread.sleep(300);
                lock1.lock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread2").start();
    }

运行程序之后,发现程序并没有停止,这个时候使用jstack查看线程的堆栈情况,会很明显的看到Found 1 deadlock:
在这里插入图片描述
这里可以查看死锁详情,thread1在等待thread2释放锁,thread2在等待thread1释放锁:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值