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

来源:互联网 发布:升级淘宝网2017新版本 编辑:程序博客网 时间:2024/05/16 23:54
  通过程序生成的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 Testjava.lang.OutOfMemoryError: Java heap spaceDumping 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.hprofReading from java_pid9900.hprof...Dump file created Sun Aug 03 15:58:32 CST 2014Snapshot read, resolving...Resolving 1262585 objects...Chasing references, expect 252 dots..........................................................................................................Eliminating duplicate references..........................................................................................................Snapshot resolved.Started HTTP server on port 7000Server is ready.

  默认端口号是7000,可以-port 指定端口号,打开7000端口:
  
  其中jhat的dump文件的结构如上。对于最后对象查询语言执行,请参考博客http://blog.csdn.net/gtuu0123/article/details/6039592
  
  上文是通过jhat命令打开访问dump文件,另外一种形式,是通过eclipse或myeclipse插件MAT来访问dump文件。
  插件地址:http://www.eclipse.org/mat/downloads.php
  其中下载下来,至于安装,大家根据插件安装方式,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官网英文地址:http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html

  既然了解到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>jps7892 Jps10588 Test9372e:\commonsoftware\jdk1.7\bin>jmap -heap 10588Attaching to process ID 10588, please wait...Debugger attached successfully.Client compiler detected.JVM version is 24.65-b04using thread-local object allocation.Mark Sweep Compact GCHeap 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% usedEden Space:   capacity = 8388608 (8.0MB)   used     = 745360 (0.7108306884765625MB)   free     = 7643248 (7.2891693115234375MB)   8.885383605957031% usedFrom Space:   capacity = 1048576 (1.0MB)   used     = 0 (0.0MB)   free     = 1048576 (1.0MB)   0.0% usedTo Space:   capacity = 1048576 (1.0MB)   used     = 0 (0.0MB)   free     = 1048576 (1.0MB)   0.0% usedtenured generation:   capacity = 10485760 (10.0MB)   used     = 0 (0.0MB)   free     = 10485760 (10.0MB)   0.0% usedPerm Generation:   capacity = 12582912 (12.0MB)   used     = 153872 (0.1467437744140625MB)   free     = 12429040 (11.853256225585938MB)   1.2228647867838542% used11722 interned Strings occupying 908888 bytes.

  jmap生成bin文件,格式如下:
E:\commonsoftware\jdk1.7\bin>jps93726248 Test12268 JpsE:\commonsoftware\jdk1.7\bin>jmap -F -dump:format=b,file=testtestdump.bin 6248Attaching to process ID 6248, please wait...Debugger attached successfully.Client compiler detected.JVM version is 24.65-b04Dumping 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{}}

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




参考文献:http://www.cnblogs.com/AloneSword/p/3821569.html
          http://blog.csdn.net/jiangtongcn/article/details/8222685
  

2 0