参考链接

Java内存溢出OOM之dump分析

1、名词解释

内存泄露:代码中的某个对象本应该被虚拟机回收,但是因为GCRoot引用而没有被回收。

内存溢出:虚拟机由于堆中拥有太多不可回收对象没有回收,导致无法继续创建新对象。

2、常见异常

java堆内存异常

java.lang.OutOfMemoryError: Java heap space

原因:异常导致-Xms或-Xmx配置不足。(注意,-Xmx 堆内存的最大大小,默认为物理内存的1/4,-Xms 堆内存的初始大小,默认为物理内存的1/64,-Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx 减去 -Xmn。

  • 使用了大量的递归或无限递归对象。
  • 使用了大量循环或死循环(循环中用到了大量的新建的对象)。
  • 使用了向数据库查询过多,可能会造成内存溢出。
  • 有数组,List,Map中存放的是对象的引用而不是对象,这些引用会让对应的对象不能被释放。会大量存储在内存中。

java栈内存异常

Exception in thread "main" java.lang.StackOverflowError

原因:对象过大或过多异常,导致-Xss配置的内存不足。(注意,设置每个线程可使用的内存大小,即栈的大小。大小设置需要注意,太小了容易栈溢出,太大了影响创建栈的数量。双刃剑,看把握。

  • 是否有递归调用。
  • 是否有大量循环或死循环。
  • 全局变量是否过多。
  • 数组、List、map数据是否过大。

方法区内存异常

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

原因:异常导致-XX:MetaspaceSize或-XX:MaxMetaspaceSize配置不足。

3、解决思路

》通过日志文件,定位异常代码位置,分析问题原因。

》当不通过日志文件主观来判断出问题代码时,通过dump文件来分析。

》上 Arthas 工具。

4、生成dump文件的三种方式

jvisualvm

说明:jvisualvm 控制台,监控时主动生成dump文件。

  • 有界面,可以指定发生 OOM 时生成 dump,可以用 everything 工具搜索保存的路径,文件名称为 java_pidxxxx.hprof。

  • jvisualvm.exe 工具在本地 java 的 jdk 安装路径,一般在 bin 目录里,如:D:\dev\java\jdk1.8.0_172\bin\jvisualvm.exe

  • 可以实时监控 cpu、内存、类、线程等的运行情况。

  • 生成的 *.hprof 文件,可以使用 MAT工具进行分析,全称:Memory Analyzer。

jmap命令

说明:使用jmap命令生成dump文件

windows环境:

jmap -dump:live,format=b,file=heap.hprof <pid>  // tasklist | grep xxx

linux环境:

jmap -dump:live,format=b,file=heap.hprof <pid>     // ps -elf | grep xxx

[zoms@ocs ~]$ jmap -dump:live,format=b,file=heap-vm.hprof 15463
Dumping heap to /home/zoms/heap-vm.hprof ...
Heap dump file created

应用启动配置

说明:在应用启动时配置添加 -XX:+HeapDumpOnOutOfMemoryError 参数。此时如果应用抛出 OutOfMemoryError 时,会自动生成 dump 文件。

目录指定:XX:HeapDumpPath。(不指定则在对应工程目录下面,找 java_pidxxxx.hprof 文件)

注:如果打开 Edit Configuration 里面,没有 VM options 的选项,可以点击 Modify options,将其添加上。

举例:

1、设置上下限分别是20,40

-Xms20m -Xmx40m -XX:+HeapDumpOnOutOfMemoryError

2、运行测试代码,生成dump文件。

3、使用MAT打开,并进行分析。

5、Memory Analyzer 分析工具

1、基础步骤:

》打开软件,File-Open Heap Dump,在开始向导中勾选第一个(Leak Suspects Report).

》Overview,内存的整体情况。

》点击下面的Reports-Leak Suspects,生成报告,查看导致内存泄露的罪魁祸首。

》根据饼图以及下方的报告说明,来分析内存泄露的具体原因。

》点击报告中Details链接,可以看到更加详细的列表说明项,其中:

Shallow Heap:为对象自身占用的内存大小,不包括它引用的对象。

Retained Heap:为当前对象大小 + 当前对象可直接或间接引用到的对象的大小总和。

2、Overview页面的常用的Actions:

Histogram:查看每个类的对象实例的个数。

详情页功能:

1)对象的引用与被引用 : 右键点击某对象 , 选择 List objects 选项 ;

》查看该对象引用了哪些对象 : 选择 with incoming reference 选项 ;

》查看该对象被哪些对象引用 : 选择 with outgoing reference 选项 ;

2)查看对象到GC Roots的最短距离:

在右键菜单中选择 " Merge Shortest Paths to GC Roots " , 这里就可以看到为什么对象可达性分析时 , 某些对象应该释放 , 却仍然存在与 GC Root 对象之间的引用链 ;

GC Roots 与 GC 垃圾回收:存在与 GC Roots 引用链导致内存泄漏 : 该对象可能与 GC Root 对象不是直接引用 , 而是由其它对象简介引用 , 导致存在这么一条引用链 ;

在查询到GC root的路径时,默认是包含所有引用的,从GC角度说,一个对象无法被GC,一定是因为有强引用存在,其它引用类型在GC需要的情况下都是可以被GC掉的,所以这里我们一般使用 exclude all phantom/weak/soft etc. references,即只查看GC路径上的强引用。

上图标识了从当前对象到GC roots的路径,这个路径解释了为什么当前对象还能存活,对分析内存泄露很有帮助。

posted on 2023-07-22 14:48  彦承  阅读(120)  评论(0编辑  收藏  举报