虚拟机堆转储快照生成以及分析

通过程序生成的dump文件来分析故障原因所在。本文给大家展示堆转储快照生成以及分析过程。
第一种:使用暴力手段来生成dump文件—-XX:+HeapDumpOnOutOfMemoryError参数
测试的类如下:
import java.util.*;
public class Test{
   public static void main(String[] args) throws Exception{
 List<TestObject> list=new ArrayList<TestObject>();
 while(true){
    list.add(new TestObject());
 }
   }
   static class TestObject{}
}

测试之前分析:这是个死循环,程序内存肯定不够用,因为他一直没有释放,所以肯定会报错。
运行此程序:
E:\javatest\test>java -Xms20M -Xmx20M -Xmn10M  -XX:+HeapDumpOnOutOfMemoryError Test
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid9900.hprof ...
Heap dump file created [29679220 bytes in 0.413 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Unknown Source)
        at java.util.Arrays.copyOf(Unknown Source)
        at java.util.ArrayList.grow(Unknown Source)
        at java.util.ArrayList.ensureExplicitCapacity(Unknown Source)
        at java.util.ArrayList.ensureCapacityInternal(Unknown Source)
        at java.util.ArrayList.add(Unknown Source)
        at Test.main(Test.java:6)
其中设置此虚拟机的堆初始值xms和最大值xmx相同,防止他自动扩建,xmn参数是指分配新生代大小。
自动生成的dump文件java_pid9900.hprof.打开此hprof文件:
若有时dump文件很大,需要设置jhat参数:jhat -J-Xmx2048m <heap dump file>,但是默认情况下是1024m。
E:\javatest\test>jhat java_pid9900.hprof
Reading from java_pid9900.hprof...
Dump file created Sun Aug 03 15:58:32 CST 2014
Snapshot read, resolving...
Resolving 1262585 objects...
Chasing references, expect 252 dots............
...............................................
...............................................
Eliminating duplicate references...............
...............................................
............................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

默认端口号是7000,可以-port 指定端口号,打开7000端口:
其中jhat的dump文件的结构如上。对于最后对象查询语言执行,请参考博客http://blog.csdn.net/gtuu0123/article/details/6039592
上文是通过jhat命令打开访问dump文件,另外一种形式,是通过eclipse或myeclipse插件MAT来访问dump文件。
其中下载下来,至于安装,大家根据插件安装方式,eclipse或myeclipse其实是一样,其中我都喜欢手动安装插件,使用link方式。
本文测试是myeclipse10.7.1,mat插件1.2.1.
上文是通过dos运行的,并且java运行时配置运行参数,根据上文的配置参数,在IDE中同样配置:


打开hprof文件,File—Open:
选择打开的report:
打开后,刷新refresh项目,项目下会生成一系列文件:
默认打开的Leak report:
Leak Report:内存泄露报告。既然有泄露图,则说明程序中存在内存泄露。先解释一下内存泄露和内存溢出区别。
内存溢出:可以这样理解,对象处于存活状态,然而内存分配不够,对于这种状况,需要检查代码对象生命周期。
内存泄露:针对溢出而言,GC无法回收对象,对象占用内存无法释放。既然提到GC无法回收对象,那就应该理解GC是如何回收对象的,在此简单通俗而言,GC会从root 引用链向下检查回收,被root引用链指向的对象都可以自动回收。但是若内存中存在这样两个对象,互相引用,而两个对象都没有被root引用(直接或间接),那么GC是无法检测到这种引用状况,因此会产生内存泄露。所以,对于内存泄露来说,内存中对象状态不一定是死亡状态。
根据Leak Report图,发现总共Total堆大小14.3(我设置了xms和xmx都是20m,不知为啥total是14.3而非20m,并且通过Problem suspect1中main占用大小,可以得知,这个图大小是个股估值而非精确值)
总共堆大小14.3m,其中a占用13.9m,剩下363b,那我们继续看a的占用情况。
a区域Problem Suspect1当前线程main方法占用97.51%,继续查看detail信息:



Shortest Path to the accumulated point:最短距离到聚集点
main线程的Shallow Heap104b,而Retained Heap14m。
堆积的对象图中:class main下有个Object[]类,其中Shallow Heap4m,而Retained Heap14m。并且Object[]引用的类就是咱们程序中定义的TestObject空静态类了。其中TestObject类的Shallow Heap8b,Retained Heap也是8b。
并且Accumulated Object by Class中,TestObject对象达到1,215,487个,占用堆大小9,723,896字节。
对于以上信息中经常提到Shallow Heap大小和Retained Heap大小,在此解释一下其含义。
Shallow Heap:对象自身占用的大小。不包括引用的对象。
Retained Heap:是指当对象本身被GC后,Heap上共释放的大小。其中Retained Heap=自身占用大小(Shallow Heap)+当前对象直接引用或间接引用的对象的大小。

既然了解到Shallow Heap和Retained Heap含义,那上文的TestObject为啥Shallow Heap=8B。那就要了解内存堆中一个对象如何存放的。
一个对象格式=A+B+C+D;
A:对象头:占用很少信息,描述Object当前状态,在Hot Spot虚拟机中,一个普通对象,占用8 bytes;数组,占用 12 bytes,包含普通对象的 8 bytes + 4 bytes(数组长度)
B:基本类型:boolean、byte 占用 1 byte,char、short 占用 2 bytes,int、float 占用 4 bytes,long、double 占用 8 bytes
C: 引用类型:4 bytes
D:填充物,在Hotspot中,每个对象占用的总空间是以8的倍数计算的,对象占用总空间(对象头+声明变量)不足8的倍数时候,自动补齐。而,这些被填充的空间,我们可以称它为“填充物”。
TestObject中就是一个空类,所以只有8bytes。
若TestObject类中有一个String name属性,则占用大小=对象头8bytes+引用类型4bytes
若TestObject类中有一个boolean isRead属性,则占用大小=对象头8bytes+boolean基本类型1byes(因为是9bytes,不是8的倍数,所以需要补齐,因此填充物=7bytes)+7bytes填充物
若TestObject类中有一个String name属性,一个int age属性,则占用大小=对象头8bytes+name属性4bytes+age属性4bytes=16bytes

除了上文中的Leak Report,mat中还有很多视图,打开i-Overview概要
这是通过参数生成的dump文件,那第二种形式则通过优雅方式jmap方式。
第二种方式:jmap生成dump文件:
jmap查看堆情况:
e:\commonsoftware\jdk1.7\bin>jps
7892 Jps
10588 Test
9372

e:\commonsoftware\jdk1.7\bin>jmap -heap 10588
Attaching to process ID 10588, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 24.65-b04

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 20971520 (20.0MB)
   NewSize          = 10485760 (10.0MB)
   MaxNewSize       = 10485760 (10.0MB)
   OldSize          = 4194304 (4.0MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 12582912 (12.0MB)
   MaxPermSize      = 67108864 (64.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 9437184 (9.0MB)
   used     = 745360 (0.7108306884765625MB)
   free     = 8691824 (8.289169311523438MB)
   7.898118760850695% used
Eden Space:
   capacity = 8388608 (8.0MB)
   used     = 745360 (0.7108306884765625MB)
   free     = 7643248 (7.2891693115234375MB)
   8.885383605957031% used
From Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
To Space:
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
tenured generation:
   capacity = 10485760 (10.0MB)
   used     = 0 (0.0MB)
   free     = 10485760 (10.0MB)
   0.0% used
Perm Generation:
   capacity = 12582912 (12.0MB)
   used     = 153872 (0.1467437744140625MB)
   free     = 12429040 (11.853256225585938MB)
   1.2228647867838542% used

11722 interned Strings occupying 908888 bytes.

jmap生成bin文件,格式如下:
E:\commonsoftware\jdk1.7\bin>jps
9372
6248 Test
12268 Jps

E:\commonsoftware\jdk1.7\bin>jmap -F -dump:format=b,file=testtestdump.bin 6248
Attaching to process ID 6248, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 24.65-b04
Dumping heap to testtestdump.bin ...
Heap dump file created

解释:在dos下运行,运行很快,jps还没捕捉到pid呢,java已经运行完毕,如何办呢,java方法如何监控呢,无论使用纯文本jmap还是可视化的jconsole,都需要一个pid,其实在方法中先让程序sleep一会,然后捕捉到pid,再进行监控。
import java.util.*;
public class Test{
   public static void main(String[] args) throws Exception{
      Thread.sleep(20000);
       List<TestObject> list=new ArrayList<TestObject>();
       while(true){
          list.add(new TestObject());
       }
   }
   static class TestObject{}
}

工欲善其事必先利其器,方可事半功倍。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值