王海鱼 发表于 2024-5-19 06:21:53

一次通过dump文件分析OutOfMemoryError异常代码定位过程

OutOfMemoryError是Java程序中常见的异常,通常出如今内存不足时,导致程序无法运行。
当出现OutOfMemoryError异常时,大概的征象是如许的。

[*]程序异常终止:OutOfMemoryError 通常会导致程序异常终止。JVM 无法为新对象分配内存时,会抛出该异常。
[*]堆内存不足:OutOfMemoryError 表示堆内存不足以为新对象分配空间。这大概会导致应用程序无法继续正常运行。
[*]内存泄漏:OutOfMemoryError 偶然会暗示存在内存泄漏问题。即使没有明显的内存泄漏,也大概是应用程序中某些对象连续增加,导致堆空间耗尽。
[*]堆转储文件:在抛出 OutOfMemoryError 异常时,JVM 大概会生成一个堆转储文件(heap dump),记载当前堆内存的状态。可以使用该文件来分析内存使用情况和定位问题。
[*]性能下降:在出现内存不足的情况下,应用程序大概会经历性能下降,由于 JVM 大概会频繁执行垃圾回收以尝试开释内存。
[*]日记记载: 日记文件中发现 OutOfMemoryError 。异常消息通常会包罗一些有关内存分配失败的信息,例如 "Java heap space"(堆空间不足)或 "GC overhead limit exceeded"(垃圾回收开销过大)。
[*]程序假死:当 JVM 的堆空间不足以分配新对象时,大概会触发垃圾回收。如果垃圾回收器尝试回收内存但无法开释充足的空间,大概由于频繁的垃圾回收导致系统资源被耗尽,程序大概会出现假死状态。表现为历程还在,但是无响应、长时间停顿。
大概的堆栈信息是如许的。
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at demo.OOMDemo.main(OOMDemo.java:22)借助MAT工具和内存泄漏产生的dump文件可以分析大概的内存泄漏代码问题定位。
什么是OutOfMemoryError异常

在 Java 中,OutOfMemoryError 是一种错误(Error),而不是异常(Exception)。
它表示 Java 虚拟机(JVM)已经耗尽了可用的内存资源,无法再分配给新的对象,导致程序无法继续执行。
OutOfMemoryError 大概由以下几种情况引起:

[*]堆内存溢出(Heap Space):当 Java 程序中创建了太多的对象,而堆内存无法满足这些对象的需求时,就会发生堆内存溢出。这通常是由于程序中存在内存泄漏(Memory Leak)大概处理大量数据时没有及时开释内存导致的。
[*]方法区溢出(PermGen Space 或 Metaspace):Java 虚拟机中的方法区用于存储类的元数据信息、静态变量、常量池等数据。当加载的类过多大概字符串常量过多时,方法区大概会溢出。在 Java 8 及之前的版本中使用的是 PermGen Space(永世代),而在 Java 8 及之后的版本中使用的是 Metaspace。溢出时会抛出相应的错误:PermGen space 或 Metaspace。
[*]栈溢出(Stack Overflow):每个线程在 Java 虚拟机中都有本身的栈空间,用于存储方法的调用栈信息。当递归调用层级过深大概方法调用过多时,栈空间大概会溢出,导致栈溢出错误。
[*]直接内存溢出:使用 NIO(New Input/Output)库举行 IO 操纵时,大概会使用到直接内存(Direct Memory)。如果程序中频繁申请直接内存而没有及时开释,大概会导致直接内存溢出。
什么是dump文件

在 Java 中,Dump 文件是指在程序发生严重问题(比如崩溃大概出现内存溢出等)时,用于记载当前 JVM 运行状态的文件。Dump 文件可以包罗有关 JVM 运行时的诊断信息,例如内存使用情况、线程堆栈信息、对象实例信息等,有助于开辟人员分析问题并定位 bug。
通常情况下,Dump 文件主要用于以下几种情况:

[*]内存溢出(OutOfMemoryError)问题分析:当程序发生内存溢出错误时,可以生成 Dump 文件以便后续分析。Dump 文件中包罗了内存堆的快照,可以检察堆中对象的分布情况,资助开辟人员找出造成内存溢出的缘故原由。
[*]JVM 崩溃问题分析:当 JVM 运行时发生崩溃,无法正常工作时,可以生成 Dump 文件以便排查问题。Dump 文件中包罗了 JVM 运行时的状态信息,例如线程状态、堆栈信息等,有助于分析问题的根本缘故原由。
[*]性能调优和分析:在举行性能调优时,Dump 文件可以提供有关 JVM 运行时的详细信息,例如线程的 CPU 占用情况、内存使用情况等,有助于分析程序的瓶颈并举行优化。
生成 Dump 文件通常需要使用 JVM 提供的工具大概下令行参数。例如,可以使用以下 JVM 参数来指定在发生 OutOfMemoryError 时生成 Dump 文件:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.log生成的 Dump 文件通常是二进制格式的文件,可以使用专门的工具(如 Eclipse Memory Analyzer)来打开和分析。
通太过析 Dump 文件,开辟人员可以更好地理解程序的运行情况,并找出问题所在。
什么是MAT工具

MAT(Memory Analyzer Tool)是一个用于 Java 应用程序内存分析的强大工具。它是一个开源项目,由 Eclipse 基金会提供支持。MAT 的主要功能是资助开辟人员分析 Java 程序的内存使用情况,特别是用于辨认和解决内存泄漏问题。
MAT 工具可以资助开辟人员解决以下范例的问题:

[*]内存泄漏分析:MAT 可以通太过析 Java 堆转储(Heap Dump)文件来辨认内存泄漏问题。它可以表现对象实例之间的引用关系,并资助开辟人员找出未被精确开释的对象,从而定位内存泄漏的根本缘故原由。
[*]内存使用情况分析:MAT 可以提供详细的内存使用情况陈诉,包罗对象实例数量、对象大小、对象引用关系等信息。这有助于开辟人员相识 Java 应用程序的内存使用模式,并举行优化。
[*]GC 日记分析:MAT 可以分析 JVM 输出的垃圾回收(GC)日记文件,资助开辟人员相识 GC 活动的情况,包罗 GC 频率、停顿时间、对象分配速率等信息。
[*]线程分析:MAT 可以提供线程转储(Thread Dump)文件的分析,资助开辟人员辨认死锁、线程壅闭等问题,并定位问题的缘故原由。
MAT 提供了一个直观的用户界面,可以通过图形化界面举行内存分析和问题定位。它还提供了一系列的分析工具和陈诉,资助开辟人员深入理解 Java 应用程序的内存举动。
搜索引擎搜索 Eclipse Memory Analyzer Tool可以找到下载链接。(外链考核很严格~~)
https://img2024.cnblogs.com/blog/1684206/202405/1684206-20240506180921479-823915655.png
异常发生了定位异常代码

使用 MAT 定位 OutOfMemoryError(OOM)的过程通常包罗以下步调:
收集堆转储文件:首先,需要在发生 OutOfMemoryError 异常时收集 Java 应用程序的堆转储文件。可以通过在 JVM 启动参数中添加 -XX:+HeapDumpOnOutOfMemoryError 来实现,在发生 OOM 异常时会自动生成堆转储文件。
打开 MAT 工具:打开 Memory Analyzer Tool(MAT)工具,并导入之前收集到的堆转储文件。通常,堆转储文件的格式是 .hprof。
执行内存分析:在 MAT 中,可以执行各种内存分析操纵,以定位导致 OutOfMemoryError 异常的缘故原由。以下是一些常见的分析步调:

[*]内存泄漏分析:使用 MAT 的 Leak Suspects 或 Dominator Tree 功能来查找大概导致内存泄漏的对象或对象组。这些功能会表现对象实例之间的引用关系,资助确定哪些对象未被精确开释。
[*]对象分布分析:检察对象分布陈诉,相识不同范例的对象在堆中的分布情况。这有助于确定哪些范例的对象占用了大量的内存空间。
[*]最大对象分析:使用 Histogram 功能检察堆中最大的对象实例,这些对象大概是导致内存问题的主要缘故原由。
[*]执行代码路径分析:如果堆转储文件包罗了充足的信息,MAT 可以尝试生成代码路径以资助确定哪些代码路径导致了内存问题。
定位异常代码:在举行内存分析的过程中,可以尝试定位导致 OutOfMemoryError 异常的相关代码。根据分析结果,可以检察对象的引用关系,确定哪些代码路径导致了内存泄漏大概内存消耗过大的问题。
异常没有发生定位异常代码

异常没有发生定位异常代码,需要通过jmap生成dump文件。
然后将其导入到 MAT 中举行分析。以下是生成堆转储文件的步调:

[*]确定 Java 历程 ID:首先,需要确定正在运行的 Java 历程的历程 ID(PID)。可以使用 jps 下令检察正在运行的 Java 历程及其 PID。
[*]生成堆转储文件:使用 jmap 下令生成堆转储文件。下令格式如下:
jmap -dump:file=<文件路径> <PID>例如,要生成名为 heapdump.hprof 的堆转储文件,可以执行以下下令:
jmap -dump:file=heapdump.hprof <PID>这将在当前工作目次下生成一个名为 heapdump.hprof 的堆转储文件。

[*]导入堆转储文件到 MAT:将生成的堆转储文件导入到 MAT 中举行分析。打开 MAT,然后选择 File -> Open Heap Dump,然后选择生成的堆转储文件。
[*]执行内存分析:一旦堆转储文件被导入到 MAT 中,就可以执行内存分析,按照前面提到的步调来查找内存问题。
通过这些步调可以手动生成堆转储文件并使用 MAT 举行分析,即使没有在 OutOfMemoryError 发生时自动生成堆转储文件也可以找到问题所在。
验证demo

首先通过一段测试代码来模拟OutOfMemoryError异常。
import java.util.ArrayList;
import java.util.List;

/**
* 用于验证oom异常
* jvm启动参数-Xmx200m -Xms200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof
*
* @author nine
* @since 1.0
*/
public class OOMDemo {

    public static void main(String[] args) {
      List<Object> listMock = new ArrayList<>();
      List<Object> list = new ArrayList<>();
      while (true) {
            // 此处代码用于创造oom错误
            list.add(new byte);
            // 此处代码是干扰代码,因为清空了变量不会内存泄漏
            listMock.add(new byte);
            listMock.clear();
      }
    }
}启动程序运行,增加jvm参数 -Xmx200m -Xms200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof。其中堆内存大小为200M,便于复现问题。
等待一段时间后,程序会抛出OutOfMemoryError异常。
java.lang.OutOfMemoryError: Java heap space
Dumping heap to heapdump.hprof ...
Heap dump file created
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at demo.OOMDemo.main(OOMDemo.java:20)同时可以在classpath下看到heapdump.hprof堆转储文件。
打开MAT,选择 File>Open Heap Dump>选择heapdump.hprof>Leak Suspects Report。
MAT会分析大概的几个问题,标题是 Problem Suspect 1等。
https://img2024.cnblogs.com/blog/1684206/202405/1684206-20240506180932558-933228080.png
由于此处只有一段代码,分析出来的问题也就一个大概问题。
The thread java.lang.Thread @ 0xf45310d0 main keeps local variables with total size 204,667,384 (98.35%) bytes.
The memory is accumulated in one instance of “java.lang.Object[]”, loaded by “<system class loader>”, which occupies 204,666,704 (98.35%) bytes.
Significant stack frames and local variables
•demo.OOMDemo.main([Ljava/lang/String;)V (OOMDemo.java:20)◦java.util.ArrayList @ 0xf45930a8 retains 204,666,728 (98.35%) bytes


The stacktrace of this Thread is available. See stacktrace. See stacktrace with involved local variables.

Keywords
java.lang.Object[]
demo.OOMDemo.main([Ljava/lang/String;)V
OOMDemo.java:20

Details »点击See stacktrace链接可以看到堆栈信息。
main
at java.lang.OutOfMemoryError.<init>()V (OutOfMemoryError.java:48)
at demo.OOMDemo.main(这也就是发生异常的代码位置。通过修改第20行代码,将list.add(new byte)注释掉,可以发现oom错误消散。
注:一般堆转储文件很大,大概需要mat的启动参数来举行大文件分析。
# 打开 MemoryAnalyzer.ini 文件
# 修改启动参数为 -Xmx2048m
-startup
plugins/org.eclipse.equinox.launcher_1.6.600.v20231106-1826.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.800.v20231003-1442
-vmargs
--add-exports=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
-Xmx2048m发生OutOfMemoryError的解决办法

解决 OutOfMemoryError 异常的方法取决于具体情况和根本缘故原由。

[*]分析堆转储文件:当发生 OutOfMemoryError 异常时,可以生成堆转储文件,通太过析该文件来定位内存泄漏大概内存使用过多的缘故原由。
[*]优化代码:检查代码中是否存在内存泄漏大概不须要的对象持有,优化数据布局和算法以淘汰内存使用量。特别是要注意避免在循环中创建大量临时对象,及时开释不再需要的对象引用。
[*]检查第三方库:某些第三方库大概存在内存泄漏大概内存占用过大的问题,需要对其举行检查和优化,大概考虑更换其他库。
[*]使用更高效的数据布局和算法:选择更得当场景的数据布局和算法,以淘汰内存使用量和提高性能。
[*]分析内存使用情况:定期监控应用程序的内存使用情况,及时发现潜在的问题并采取相应步伐。
[*]使用更轻量级的解决方案:偶然可以考虑使用更轻量级的框架或工具,以淘汰内存消耗。
再者可以优化内存参数:

[*]增加堆内存:通过增加 JVM 的堆内存大小来提供更多的内存空间。可以通过调解 -Xmx 和 -Xms 参数来增加堆内存的最大和初始大小。但需要注意,过大的堆内存大概会导致垃圾回收时间过长,影响程序性能。
[*]增加物理内存:如果是物理呆板内存不足导致的 OutOfMemoryError,可以考虑增加物理内存来解决问题。
关于作者

来自一线全栈程序员nine的探索与实践,连续迭代中。
接待关注大概点个小红心~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 一次通过dump文件分析OutOfMemoryError异常代码定位过程