本文主要阐述了,如何在eclipse中使用Memory Analyzer
Tool(MAT)来分析内存泄露等问题。
文章结构:
- java内存管理
- 本地内存
- Java中的内存
- Java堆
- Java栈
- 逃逸分析
- 内存泄露
- 在Eclipse中分析内存泄露
- 堆转储
- eclipse中的安卓堆转储分析
- 使用MAT(Memory Analyzer
Tool)
- 安装
1.
Java中的内存管理
java将内存大致地分成了两个区域,堆和栈。我们首先大概了解一下,一般的电脑中的内存结构,然后在介绍java中堆和栈
1.1
本地内存
本地内存是提供给进程运行内存的区域,比如:java进程。本地的内存由操作系统控制,主要包括:物理内存,和其他物理设备,诸如:光盘、闪存。
计算机的处理器负责计算将要被处理的指令,并将计算结构存储到寄存器之中。这些存储数据的寄存器都是一些快速记忆元件。处理器通过存储总线可以访问正规存储器。CPU能够访问的内存量由物理地址的大小所决定。一个16位的地址可以访问2^16
= 65536内存位置。一个32位的地址可以访问2^32 =
4294967296内存位置。如果每一个内存区域由8个字节组成,那么一个16位的系统可以访问64Kb的内存而32位的系统可以访问4GB的内存。
操作系统通常会用虚拟内存来表示物理内存和进程使用的内存之间的映射。操作系统给每一个进程在虚拟内存空间分配内存,然后将分配的虚拟内存访问和真是的物理地址相互映射。
现在的32位系统通常使用物理地址扩展(PAE)将物理空间扩展成36位操作系统,这样操作系统实际可以访问的内存将会达到64GB。然而,操作系统使用虚拟内存最多允许单个进程占用4G的内存。即使启用了PAE也无法访问超出4G内存。
显然,在64位系统中,4GB的内存限制将不复存在。
1.2
Java中的内存
Java为了方便使用内存而进行了管理。新的对象的创建和放置在Java的堆内存中。一旦你的Java应用程序不在持有该对象的任何引用,此时Java的垃圾收集器将会进行内存清理,释放掉该内存,以便于再次使用。
1.3
Java堆
在堆中,JVM存储了Java程序创建的所有的对象,比如:任何new出来的对象。java的垃圾收集器会将堆分成两个逻辑区域,如此它便可以更加快速的进行垃圾识别和清理。
新对象的内存运行时在堆上被创建。
1.4
Java栈
一系列的方法调用和局部变量被存储在Java栈中,当一个方法被调用的时候,它的栈帧将会被放到调用栈的最上方。栈帧保存了方法的状态:正在执行的代码行,所有的局部变量的值。处于栈顶的方法始终是当前正在执行的方法。每一个线程都拥有自己的调用栈。
1.5
逃逸分析
如前面所述,Java的对象都是在堆上被创建和存储。编程语言不允许程序员去决定一个对象是否可以存放于栈上。但是在某些情况下,最好能够在栈上分配对象内存,因为栈上的内存分配比堆上更加的廉价。栈上的内存重新分配时没有代价的,并且栈可以再运行时被高效的管理。
因此,Jvm可以使用内部的逃逸分析来检查,对象是否仅仅在一个线程或者方法内部被引用。在这种情况下,JVM可能会允许在栈上进行对象的创建,来提升java程序的性能。
1.6
内存泄露
只要一个对象在程序中不存在任何的引用,Java的垃圾收集器就会从内存释放该对象。。反之,则不会。
2.1
堆转储
Java的堆转储是指在某一个特定的时刻,完整的java对象图的一个快照。它以后缀名为HPROF的二进制形式保存。
存储内容包括:对象,域变量,原始类型和对象引用。
Eclipse
MAT是一个用来可视化基于堆转储文件对象引用并提供分析潜在内存泄露问题的工具集。
我们可以通过指令来让JVM在堆内存溢出时自动创建堆转储文件。为了让JVM自动穿件堆转储文件,我们可以设置JVM参数为:-XX:+HeapDumpOnOutOfMemoryError。
略。
2.3
使用Eclipse Memory Analyzer
当一个后缀名为hprof文件被创建的时候,可以通过双击在eclispe之中打开(安装教程自行google)。并选择Leak Suspects
Report,如下图为主界面。
这个页面显示了整体的概览,我们可以从这里入手分析可能的内存泄露的地方。支配树可以快速的给出被使用的对象,方便我们调试使用。
PS:
Shallow
Size
对象自身占用的内存大小,不包括它引用的对象。
针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。
针对数组类型的对象,它的大小是数组元素对象的大小总和。
Retained
Size
Retained
Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C,
C就是间接引用)
换句话说,Retained
Size就是当前对象被GC后,从Heap上总共能释放掉的内存。
不过,释放的时候还要排除被GC
Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。