成都心情

  BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  98 随笔 :: 2 文章 :: 501 评论 :: 1 Trackbacks

前言的前言:本文是自20058月以来,首次在一个月之内发布三篇文章。谨以此文献给这么多年始终不济的我。所谓少不入川,而今已非年少。北漂快两年了,何时能回到故乡,回去后又会怎样,也许永远是个未知……

 

前言

 

在平时工作过程中,有时会遇到OutOfMemoryError,我们知道遇到Error一般表明程序存在着严重问题,可能是灾难性的。所以找出是什么原因造成OutOfMemoryError非常重要。现在向大家引荐Eclipse Memory Analyzer tool(MAT),来化解我们遇到的难题。如未说明,本文均使用Java 5.0 on Windows XP SP3环境。

 

为什么用MAT

 

之前的观点,我认为使用实时profiling/monitoring之类的工具,用一种非常实时的方式来分析哪里存在内存泄漏是很正确的。年初使用了某profiler工具测试消息中间件中存在的内存泄漏,发现在吞吐量很高的时候profiler工具自己也无法响应,这让人很头痛。后来了解到这样的工具本身就要消耗性能,且在某些条件下还发现不了泄漏。所以,分析离线数据就非常重要了,MAT正是这样一款工具。

 

为何会内存溢出

 

我们知道JVM根据generation()来进行GC,根据下图所示,一共被分为young generation(年轻代)tenured generation(老年代)permanent generation(永久代, perm gen)perm gen(或称Non-Heap 非堆)是个异类,稍后会讲到。注意,heap空间不包括perm gen


绝大多数的对象都在young generation被分配,也在young generation被收回,当young generation的空间被填满,GC会进行minor collection(次回收),这次回收不涉及到heap中的其他generationminor collection根据weak generational hypothesis(弱年代假设)来假设young generation中大量的对象都是垃圾需要回收,minor collection的过程会非常快。young generation中未被回收的对象被转移到tenured generation,然而tenured generation也会被填满,最终触发major collection(主回收),这次回收针对整个heap,由于涉及到大量对象,所以比minor collection慢得多。

 

JVM有三种垃圾回收器,分别是throughput collector,用来做并行young generation回收,由参数-XX:+UseParallelGC启动;concurrent low pause collector,用来做tenured generation并发回收,由参数-XX:+UseConcMarkSweepGC启动;incremental low pause collector,可以认为是默认的垃圾回收器。不建议直接使用某种垃圾回收器,最好让JVM自己决断,除非自己有足够的把握。

 

Heap中各generation空间是如何划分的?通过JVM-Xmx=n参数可指定最大heap空间,而-Xms=n则是指定最小heap空间。在JVM初始化的时候,如果最小heap空间小于最大heap空间的话,如上图所示JVM会把未用到的空间标注为Virtual。除了这两个参数还有-XX:MinHeapFreeRatio=n -XX:MaxHeapFreeRatio=n来分别控制最大、最小的剩余空间与活动对象之比例。在32Solaris SPARC操作系统下,默认值如下,在32windows xp下,默认值也差不多。


参数

默认值

MinHeapFreeRatio

40

MaxHeapFreeRatio

70

-Xms

3670k

-Xmx

64m


由于tenured generationmajor collection较慢,所以tenured generation空间小于young generation的话,会造成频繁的major collection,影响效率。Server JVM默认的young generationtenured generation空间比例为1:2,也就是说young generationedensurvivor空间之和是整个heap(当然不包括perm gen)的三分之一,该比例可以通过-XX:NewRatio=n参数来控制,而Client JVM默认的-XX:NewRatio8。至于调整young generation空间大小的NewSize=nMaxNewSize=n参数就不讲了,请参考后面的资料。

 

young generation中幸存的对象被转移到tenured generation,但不幸的是concurrent collector线程在这里进行major collection,而在回收任务结束前空间被耗尽了,这时将会发生Full Collections(Full GC),整个应用程序都会停止下来直到回收完成。Full GC是高负载生产环境的噩梦……

 

现在来说说异类perm gen,它是JVM用来存储无法在Java语言级描述的对象,这些对象分别是类和方法数据(与class loader有关)以及interned strings(字符串驻留)。一般32OSperm gen默认64m,可通过参数-XX:MaxPermSize=n指定,JVM Memory Structure一文说,对于这块区域,没有更详细的文献了,神秘。

 

回到问题“为何会内存溢出?”。

要回答这个问题又要引出另外一个话题,既什么样的对象GC才会回收?当然是GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。通常GC Roots是一个在current thread(当前线程)call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。所以GC Roots是分析对象为何还存活于内存中的利器。知道了什么样的对象GC才会回收后,再来学习下对象引用都包含哪些吧。

 

从最强到最弱,不同的引用(可到达性)级别反映了对象的生命周期。

l  Strong Ref(强引用):通常我们编写的代码都是Strong Ref,于此对应的是强可达性,只有去掉强可达,对象才被回收。

l  Soft Ref(软引用):对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。

l  Weak Ref(弱引用):比Soft Ref更弱,当发现不存在Strong Ref时,立刻回收对象而不必等到内存吃紧的时候。通过java.lang.ref.WeakReferencejava.util.WeakHashMap类实现。

l  Phantom Ref(虚引用):根本不会在内存中保持任何对象,你只能使用Phantom Ref本身。一般用于在进入finalize()方法后进行特殊的清理过程,通过 java.lang.ref.PhantomReference实现。

 

有了上面的种种我相信很容易就能把heapperm gen撑破了吧,是的利用Strong Ref,存储大量数据,直到heap撑破;利用interned strings(或者class loader加载大量的类)把perm gen撑破。

 

关于shallow sizeretained size

 

Shallow size就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。在32位系统上,对象头占用8字节,int占用4字节,不管成员变量(对象或数组)是否引用了其他对象(实例)或者赋值为null它始终占用4字节。故此,对于String对象实例来说,它有三个int成员(3*4=12字节)、一个char[]成员(1*4=4字节)以及一个对象头(8字节),总共3*4 +1*4+8=24字节。根据这一原则,对String a=”rosen jiang”来说,实例ashallow size也是24字节(很多人对此有争议,请看官甄别并留言给我)。

 

Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。为了更好的理解retained size,不妨看个例子。

 

把内存中的对象看成下图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,正解!这就是reference chain的起点。

retained_objects.gifretained_objects_2.gif

obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。

所以对于左图,obj1retained sizeobj1obj2obj4shallow size总和;右图的retained sizeobj1obj2obj3obj4shallow size总和。obj2retained size可以通过相同的方式计算。

 

Heap Dump

 

heap dump是特定时间点,java进程的内存快照。有不同的格式来存储这些数据,总的来说包含了快照被触发时java对象和类在heap中的情况。由于快照只是一瞬间的事情,所以heap dump中无法包含一个对象在何时、何地(哪个方法中)被分配这样的信息。

 

在不同平台和不同java版本有不同的方式获取heap dump,而MAT需要的是HPROF格式的heap dump二进制文件。想无需人工干预的话,要这样配置JVM参数:-XX:-HeapDumpOnOutOfMemoryError,当错误发生时,会自动生成heap dump,在生产环境中,只有用这种方式。如果你想自己控制什么时候生成heap dump,在Windows+JDK6环境中可利用JConsole工具,而在Linux或者Mac OS X环境下均可使用JDK56自带的jmap工具。当然,还可以配置JVM参数:-XX:+HeapDumpOnCtrlBreak,也就是在控制台使用Ctrl+Break键来生成heap dump。由于我是windows+JDK5,所以选择了-XX:-HeapDumpOnOutOfMemoryError这种方式,更多配置请参考MAT Wiki

 

参考资料

 

MAT Wiki

Interned Strings

Strong,Soft,Weak,Phantom Reference

Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

Permanent Generation

Understanding Weak References译文

Java HotSpot VM Options

Shallow and retained sizes

JVM Memory Structure

GC roots


请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处: http://www.blogjava.net/rosen

posted on 2010-05-21 20:59 Rosen 阅读(75837) 评论(23)  编辑  收藏 所属分类: Java 基础

评论

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2010-05-23 18:20 BeanSoft
好好干! 前途是光明的! 当然, 在京买房就别考虑了, 不现实!  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2010-05-24 09:51 Rosen
@BeanSoft
感谢BeanSoft兄关心,主要还是不会混,也错过一次次的机会。
是啊,是要为后路考虑了。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2010-05-25 15:02 liucr
很精辟,感谢博主,你的前途一定会有。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2010-12-15 09:15 Rosen
@李文栋
我是成都人,目前还是有6年工作时间了。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2010-12-15 16:55 Rayleeya
@Rosen
多谢大虾的博文。
  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2011-01-26 17:31 耿恬
不错,受益匪浅。楼主这能力不要担心前途  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2011-04-21 19:51 morning5607
膜拜,同是成都人!  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2011-08-29 16:10 lijunwyf22
很不错,值得收藏。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2011-10-19 18:14 fan
嘻嘻  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2012-03-12 14:11 Locke
首先谢谢楼主关于MAT的介绍。
有一点,-XX:+HeapDumpOnCtrlBreak这个参数在JDK6里面应该是被取消了。我本地是JDK6_29版本。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一)[未登录] 2012-06-27 11:23 IT民工
最后也没说。是什么引发内存泄露。。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2012-07-18 17:40 ureygo
说的不错,长知识了  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一)[未登录] 2012-11-19 11:55 william
有一个问题想要请教一下:在图2中假设

obj1的shallow size = 1024b,
obj2的shallow size = 1024b,
obj3的shallow size = 1024b,
obj4的shallow size = 1024b, 那么,按照您的算法:

obj1的retain size = 4096b,
obj2的retain size = 3072b,
obj3的retain size = 1024b,
obj3的retain size = 1024b,

那实际的内存占用大小是多少呢?

1:4096+3027+1024+1024?
2:1024+4*1 + 1024+4*2 + 1024 + 1024?

  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2012-12-19 09:37 shuhucy
-XX:-HeapDumpOnOutOfMemoryError

这个配置是错的,正确的配置是:

-XX:+HeapDumpOnOutOfMemoryError

这样才会打印堆转储文件

同时建议配置:
-XX:HeapDumpPath=D:\heapdump选项,指定文件目录,不然转储文件输出到什么地方了,很多人找不到

  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2013-06-16 22:09 woyaowenzi
第一张图没了。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2014-04-15 17:46 janeni_s
很老厉害的博文 膜拜  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2014-09-17 19:01 chenlian
我达州人,呵呵  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2014-09-17 19:01 chenlian
QQ584314183  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2014-10-31 16:58 liyghting
@woyaowenzi看了下源码,引用的是sun公司的,估计地址换了,就没有了。  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一)[未登录] 2014-12-24 16:31 dd
@BeanSoft
  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一)[未登录] 2015-04-20 15:02 yoyo
赞一个  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2015-11-11 15:36 JDongfeng
好文章,顶一个  回复  更多评论
  

# re: 使用Memory Analyzer tool(MAT)分析内存泄漏(一) 2016-06-14 11:17 袁良锭
小瑕疵。
图片显示不了。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: