深入浅出JVM之Java堆分析

一、内存溢出(OOM)的原因

jvm中可能引起内存溢出的内存区域有堆、永久区、线程栈和直接内存。其中堆保存了所有的对象的内容,永久区保存对象的信息。这四个区域的内存空间得不到满足都有可能导致内存溢出。

堆溢出

由于对象不断的占用分配的空间,而这些对象的引用也没有及时的释放,堆的空间不断被占用,最终导致内存溢出。解决办法是增大堆空间,及时释放内存(减少对象被引用的时间)

永久区

由于系统中类的数量太多,由于类的元信息是保持在永久区的,当所有的类的元信息加起来超过永久区的内存大小时,会导致永久区的内存溢出。

解决方法:增大Perm区,允许Class回收

Java栈溢出

jvm在创建线程的时候,需要为线程分配栈空间,而这个空间和堆空间是不同的,需要直接向操作系统申请,而如果操作系统无法给出足够的空间时,就会抛出OOM。

解决方法:减少堆空间,减少线程栈大小,减少线程的数量(使用线程池)

直接内存溢出

通过ByteBuffer.allocateDirect()向操作系统直接申请内存,如果操作系统的内容无法满足内存分配的时候就会抛出内存溢出的异常。

解决方法:减少堆空间,有意出发GC

二、MAT使用基础

MAT的全称是Memory Analyzer,它是基于Eclipse的插件,可以用来分析dump出来的堆文件。

用MAT打开一个dump堆文件,可以查看如下内容:

柱状图(Histogram)-显示当前堆中的类的统计信息,包括类的实例数,深堆和潜堆

支配树(dominator_tree)-以树形结构显示对象的被调用关系。要达到对象B,必须经过对象A,那么就称对象A支配对象B,通过对象A和C都可以到达对象B,那么对象A和C就不能支配对象B。在前一种情况下对象A被称为支配者,对象B称为被支配者。当支配者被回收的时候,被支配这也都会被回收。通过支配树可以查看堆可以被GC回收的情况。

在MAT的某一个类上选中某个类,在右键菜单的List objects可以显示outgoing reference-出引用对象,显示该对象都引用了哪些对象,这些对象的深堆和潜堆信息。ingoing reference-显示入引用对象,显示该对象被哪些对象引用。这两个功能对查找问题比较有帮助。

Shallow Heap(浅堆)和Retained Heap(深堆)

浅堆是指一个对象结构所占用的内存大小,以String为例,在MAT中可以通过类的Attributes属性看到该类的结构信息,在JDK7之后String的对象结构是两个int类型的hash值和一个引用,所占的内存大小是4*2+4=12个字节,在加上对象头的8个字节一共是20个字节。由于对象大小是按照8字节对齐的,所以一个String对象的浅堆大小是24字节。浅堆的大小与对象的内容无关,也就是所有的String对象的浅堆大小都是24字节,它至于对象的结构有关。

深堆是指一个对象被GC回收后可以真实释放的内存大小。深堆表示一个对象实际占用的内存空间的大小。它是通过支配树上该对象可以访问到的所有对象的浅堆之和来统计的。

发布了36 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/xjjdlut/article/details/105261276