使用MemoryAnalyzertoolMAT分析内存泄漏_第1页
使用MemoryAnalyzertoolMAT分析内存泄漏_第2页
使用MemoryAnalyzertoolMAT分析内存泄漏_第3页
使用MemoryAnalyzertoolMAT分析内存泄漏_第4页
使用MemoryAnalyzertoolMAT分析内存泄漏_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

使用MemoryAnalyzertoolMAT分析内存泄露使用MemoryAnalyzertoolMAT分析内存泄露14/14使用MemoryAnalyzertoolMAT分析内存泄露使用MemoryAnalyzertool(MAT)分析内存泄露(一)序言在平常工作过程中,有时会碰到OutOfMemoryError,我们知道碰到Error一般表示程序存在着严重问题,可能是灾害性的。因此找出是什么原由造成OutOfMemoryError特别重要。此刻向大家介绍EclipseMemoryAnalyzertool(MAT),来化解我们碰到的难题。如未说明,本文均使用Java5.0onWindowsXPSP3环境。为何用MAT以前的看法,我以为使用及时profiling/monitoring之类的工具,用一种特别及时的方式来分析哪里存在内存泄露是很正确的。年初使用了某profiler工具测试信息中间件中存在的内存泄漏,发此刻吞吐量很高的时候profiler工具自己也无法响应,这让人很头痛。此后认识到这样的工具自己就要耗费性能,且在某些条件下还发现不了泄露。因此,分析离线数据就特别重要了,MAT正是这样一款工具。为何会内存溢出我们知道JVM依据generation(代)来进行GC,依据以下列图所示,一共被分为younggeneration(年青代)、tenuredgeneration(老年月)、permanentgeneration(永远代,permgen),permgen(或称Non-Heap非堆)是个异类,稍后会讲到。注意,heap空间不包含permgen。绝大部分的对象都在younggeneration被分派,也在younggeneration被回收,当younggeneration的空间被填满,GC会进行minorcollection(次回收),此次回收不波及到heap中的其他generation,minorcollection依据weakgenerationalhypothesis(弱年月假定)来假定younggeneration中大批的对象都是垃圾需要回收,minorcollection的过程会特别快。younggeneration中未被回收的对象被转移到tenuredgeneration,但是tenuredgeneration也会被填满,最后触发majorcollection(主回收),此次回收针对整个heap,因为波及到大批对象,因此比minorcollection慢得多。JVM有三种垃圾回收器,分别是1throughputcollector,用来做并行younggeneration回收,由参数-XX:+UseParallelGC启;concurrentlowpausecollector,用来做tenuredgeneration并回收,由参数-XX:+UseConcMarkSweepGC启;incrementallowpausecollector,能够是默的垃圾回收器。不建直接使用某种垃圾回收器,最好JVM自己抉择,除非自己有足的掌握。Heap中各generation空是怎样区分的?通JVM的-Xmx=n参数可指定最大heap空,而-Xms=n则是指定最小heap空。在JVM初始化的候,假如最小heap空小于最大heap空的,如上所示JVM会把未用到的空注Virtual。除了两个参数有-XX:MinHeapFreeRatio=n和-XX:MaxHeapFreeRatio=n来分控制最大、最小的节余空与活象之比率。在32位SolarisSPARC操作系下,默以下,在32位windowsxp下,默也差不多。参数默认值MinHeapFreeRatio40MaxHeapFreeRatio70-Xms3670k-Xmx64m因为tenuredgeneration的majorcollection慢,因此tenuredgeneration空小于younggeneration的,会造成繁的majorcollection,影响效率。ServerJVM默的younggeneration和tenuredgeneration空比率1:2,也就是younggeneration的eden和survivor空之和是整个heap(自然不包含permgen)的三分之一,比率能够通-XX:NewRatio=n参数来控制,而ClientJVM默的-XX:NewRatio是8。至于整younggeneration空大小的NewSize=n和MaxNewSize=n参数就不了,参照后边的料。younggeneration中幸存的象被移到tenuredgeneration,但不幸的是concurrentcollector程在里行majorcollection,而在回收任束前空被耗尽了,将会生FullCollections(FullGC),整个用程序都会停止下来直到回收达成。FullGC是高生境的噩梦⋯⋯在来异permgen,它是JVM用来存无法在Java言描绘的象,些象分是和方法数据(与classloader相关)以及internedstrings(字符串留)。一般32位OS下permgen默64m,可通参数-XX:MaxPermSize=n指定,JVMMemoryStructure一文,于地区,没有更的文件了,奇异。回到“何会内存溢出?”。要回答个又要引出其余一个,既什么的象GC才会回收?自然是GC通任何referencechain(引用)无法某个象的候,象即被回收。名GCRoots正是分析一程的起点,比方JVM自己保证了象的可抵达性(那么JVM就是GCRoots),因此GCRoots就是在内存中保持象可抵达性的,一旦不能够抵达,即被回收。平常GCRoots是一个在currentthread(目前途)的callstack(用)上的象(比方方法参数和局部量),或者是程自己或许是systemclassloader(系加器)加的以及nativecode(当地代)保留的活象。因此GCRoots是分析象何存活于内存中的利器。知道了什么的象GC才会回收后,再来学下象引用都包含哪些吧。2从最强到最弱,不同样的引用(可抵达性)级别反应了对象的生命周期。StrongRef(强引用):平常我们编写的代码都是StrongRef,于此对应的是强可达性,只有去掉强可达,对象才被回收。SoftRef(软引用):对应软可达性,只需有足够的内存,就向来保持对象,直到发现内存吃紧且没有StrongRef时才回收对象。一般可用来实现缓存,经过java.lang.ref.SoftReference类实现。WeakRef(弱引用):比SoftRef更弱,当发现不存在StrongRef时,马上回收对象而不用等到内存吃紧的时候。经过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。PhantomRef(虚引用):根本不会在内存中保持任何对象,你只好使用PhantomRef自己。一般用于在进入finalize()方法后进行特其余清理过程,经过java.lang.ref.PhantomReference实现。有了上边的各种我相信很简单就能把大批数据,直到heap撑破;利用gen撑破。

heap和permgen撑破了吧,是的利用StrongRef,储蓄internedstrings(或许classloader加载大批的类)把perm对于shallowsize、retainedsizeShallowsize就是对象自己占用内存的大小,不包含对其余对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。在32位系统上,对象头占用8字节,int占用4字节,不论成员变量(对象或数组)能否引用了其余对象(实例)或许赋值为null它向来占用4字节。故此,对于String对象实例来说,它有三个int成员(3*4=12字节)、一个char[]成员(1*4=4字节)以及一个对象头(8字节),总合3*4+1*4+8=24字节。依据这一原则,对Stringa=”rosenjiang来”说,实例a的shallowsize也是24字节(好多人对此有争议,请看官甄别并留言给我)。Retainedsize是该对象自己的shallowsize,加上从该对象能直接或间接接见到对象的shallowsize之和。换句话说,retainedsize是该对象被GC此后所能回收到内存的总和。为了更好的理解retainedsize,不如看个例子。把内存中的对象看作以下列图中的节点,并且对象和对象之间相互引用。这里有一个特其余节点GCRoots,正解!这就是referencechain的起点。3从obj1下手,上图中蓝色节点代表但是只有经过obj1才能直接或间接接见的对象。因为能够经过GCRoots接见,因此左图的obj3不是蓝色节点;而在右图倒是蓝色,因为它已经被包含在retained会合内。因此对于左图,obj1的retainedsize是obj1、obj2、obj4的shallowsize总和;右图的retainedsize是obj1、obj2、obj3、obj4的shallowsize总和。obj2的retainedsize能够经过同样的方式计算。HeapDumpheapdump是特准时间点,java进度的内存快照。有不同样的格式来储蓄这些数据,总的来说包含了快照被触发时java对象和类在heap中的状况。因为快照但是一瞬时的事情,因此heapdump中无法包含一个对象在何时、何地(哪个方法中)被分派这样的信息。在不同样平台和不同样java版本有不同样的方式获得heapdump,而MAT需要的是HPROF格式的heapdump二进制文件。想无需人工干涉的话,要这样配置JVM参数:-XX:-HeapDumpOnOutOfMemoryError,当错误发生时,会自动生成heapdump,在生产环境中,只合用这类方式。假如你想自己控制什么时候生成heapdump,在Windows+JDK6环境中可利用JConsole工具,而在Linux或许MacOSX环境下均可使用JDK5、6自带的jmap工具。自然,还能够配置JVM参数:-XX:+HeapDumpOnCtrlBreak,也就是在控制台使用Ctrl+Break键来生成heapdump。因为我是windows+JDK5,因此选择了-XX:-HeapDumpOnOutOfMemoryError这类方式,更多配置请参照MATWiki。4使用MemoryAnalyzertool(MAT)分析内存泄露(二)序言在使用MemoryAnalyzertool(MAT)分析内存泄露(一)中,我介绍了内存泄露的前因结果。在本文中,将介绍MAT怎样依据heapdump分析泄露本源。因为测试典范可能过于简单,很简单找出问题,但我希望借此贯串交融。一开始不得不谈谈ClassLoader,实质上,它的工作就是把磁盘上的类文件读入内存,此后调用方法告诉系统把内存镜像办理成合法的字节码。Java供给了抽象类ClassLoader,所合用户自定义类装载器都实例化自ClassLoader的子类。systemclassloader在没有指定装载器的状况下默认装载用户类,在中既sun.misc.Launcher$AppClassLoader。更详尽的内容请参看下边的资料。准备heapdump请看下边的Pilot类,没啥特其余。/Pilotclass@authorrosenjiang*/packageorg.rosenjiang.bo;publicclassPilot{Stringname;intage;publicPilot(Stringa,intb){name=a;age=b;}}此后再看OOMHeapTest类,它是怎样撑破heapdump的。/OOMHeapTestclass@authorrosenjiang*/packageorg.rosenjiang.test;importjava.util.Date;importjava.util.HashMap;importjava.util.Map;5importorg.rosenjiang.bo.Pilot;publicclassOOMHeapTest{publicstaticvoidmain(String[]args){oom();}privatestaticvoidoom(){Map<String,Pilot>map=newHashMap<String,Pilot>();Object[]array=newObject[1000000];for(inti=0;i<1000000;i++){Stringd=newDate().toString();Pilotp=newPilot(d,i);map.put(i+"rosenjiang",p);array[i]=p;}}}是的,上边结构了好多的Pilot类实例,向数组和map中放。因为是StrongRef,GC自然不会回收这些对象,向来放在heap中直到溢出。自然在运转前,先要在Eclipse中配置VM参数-XX:+HeapDumpOnOutOfMemoryError。好了,一会儿功夫内存溢出,控制台打出以下信息。java.lang.OutOfMemoryError:JavaheapspaceHeapdumpfilecreated[78233961bytesin1.995secs]Exceptioninthread"main"java.lang.OutOfMemoryError:Javaheapspacejava_pid3600.hprof既是heapdump,能够在OOMHeapTest类所在的工程根目录下找到。MAT安装话分两端说,有了heapdump还得安装MAT。能够在选择适合的方式安装。安装达成后切换到MemoryAnalyzer视图。在Eclipse的左上角有OpenHeapDump按钮,依据刚才说的路径找到java_pid3600.hprof文件并翻开。分析hprof文件会花些时间,此后会弹出导游,直接Finish即可。稍后会看到以下列图所示的界面。6MAT工具分析了heapdump后在界面上特别直观的展现了一个饼图,该图深色区域被思疑有内存泄露,能够发现整个heap才64M内存,深色地区就占了99.5%。接下来是一个简洁的描绘,告诉我们main线程占用了大批内存,并且明确指出systemclassloader加载的"java.lang.Thread"实例有内存齐集,并建议用重点字"java.lang.Thread"进行检查。因此,MAT经过简单的两句话就说了然问题所在,就算使用者没什么办理内存问题的经验。在下边还有一个"Details"链接,在点开以前不如考虑一个问题:为何对象实例汇齐集在内存中,为何存活(而未被GC)?是的——StrongRef,那么再走近一些吧。7点击了"Details"链接此后,除了在上一页看到的描绘外,还有ShortestPathsTotheAccumulationPoint和AccumulatedObjects部分,这里说了然从GCroot到齐集点的最短路径,以及圆满的referencechain。察看AccumulatedObjects部分,java.util.HashMap和java.lang.Object[1000000]实例的retainedheap(size)最大,在上一篇文章中我们知道retainedheap代表从该类实例沿着referencechain往下所能采集到的其余类实例的shallowheap(size)总和,因此显然类实例都齐集在HashMap和Object数组中了。这里我们发现一个风趣的现象,既Object数组的shallowheap和retainedheap居然同样,经过Shallowandretainedsizes一文可知,数组的shallowheap和一般对象(非数组)不同样,依靠于数组的长度和里面的元素的类型,对数组求shallowheap,也就是求数组会合内全部对象的shallowheap之和。好,再来看对象实例的shallowheap为何是16,因为对象头是8字节,成员变量int是4字节、String引用是4字节,故总合16字节。8接着往下看,抵达了AccumulatedObjectsbyClass地区,顾名思义,这里能找到被齐集的对象实例的类名。类上头条了,被实例化了290,325次,再返回去看程序,我认但是成心这么干的。还有好多合用的报告可用来辅助分析问题,但是本文中的例子太简单,也用不上。此后如合用到,必然撰文详尽叙述。又是permgen我们在上一篇文章中知道,permgen是个异类,里面储蓄了类和方法数据(与classloader相关)以及internedstrings(字符串驻留)。在heapdump中没有包含太多的permgen信息。那么我们就用这些少许的信息来解决问题吧。看下边的代码,利用internedstrings把permgen撑破了。/OOMPermTestclass@authorrosenjiang*/packageorg.rosenjiang.test;publicclassOOMPermTest{publicstaticvoidmain(String[]args){oom();}privatestaticvoidoom(){Object[]array=newObject[10000000];for(inti=0;i<10000000;i++){Stringd=String.valueOf(i).intern();array[i]=d;}}}9控制台打印以下的信息,此后把java_pid1824.hprof文件导入到MAT。其实在MAT里,看到的状况应当和“OutOfMemoryError:Javaheapspace差不多”(用了数组),因为heapdump并无包含internedstrings方面的任何信息。但是在这里需要重申,使用intern()方法的时候应当多加注意。java.lang.OutOfMemoryError:PermGenspaceHeapdumpfilecreated[121273334bytesin2.845secs]Exceptioninthread"main"java.lang.OutOfMemoryError:PermGenspace倒是在思虑怎样把classloader撑破废了些心思。经过试一试,发现使用ASM来动向生成类才能达到目的。ASM()的主要作用是办理已编译类(compiledclass),能对已编译类进行生成、变换、分析(功能之一是实现动向代理),并且它运转起来足够的快和小巧,文档也全面,实属居家必备之良品。ASM供给了coreAPI和treeAPI,前者是鉴于事件的方式,后者是鉴于对象的方式,近似于XML的SAX、DOM分析,但是使用treeAPI性能会有损失。既然下边要用到ASM,这里不得不啰嗦下已编译类的结构,包含:1、修饰符(比方public、private)、类名、父类名、接口和annotation部分。2、类成员变量申明,包含每个成员的修饰符、名字、种类和annotation。3、方法和结构函数描绘,包含修饰符、名字、返回和传入参数种类,以及annotation。自然还包含这些方法或结构函数的详尽Java字节码。4、常量池(constantpool)部分,constantpool是一个包含类中出现的数字、字符串、种类常量的数组。10已编译类和本来的类源码差别在于,已编译类只包含类自己,内部类不会在已编译类中出现,而是生成其余一个已编译类文件;其二,已编译类中没有说明;其三,已编译类没有package和import部分。这里还得谈谈已编译类对Java种类的描绘,对于原始种类由单个大写字母表示,Z代表boolean、C代表char、B代表byte、S代表short、I代表int、F代表float、J代表long、D代表double;而对类种类的描绘使用内部名(internalname)外加前缀L和后边的分号共同表示来表示,所谓内部名就是带全包路径的表示法,比方String的内部名是java/lang/String;对于数组种类,使用单方括号加上数据元素种类的方式描绘。最后对于方法的描绘,用圆括号来表示,假如返回是void用V表示,具体参照以下列图。下边的代码中会使用ASMcoreAPI,注意接口ClassVisitor是核心,FieldVisitor、MethodVisitor都是辅助接口。ClassVisitor应当依据这样的方式来调用:visitvisitSource?visitOuterClass?(visitAnnotation|visitAttribute)*(visitInnerClass|visitField|visitMethod)*visitEnd。就是说visit方法必然第一调用,再调用最多一次的visitSource,再调用最多一次的visitOuterClass方法,接下来再多次调用visitAnnotation和visitAttribute方法,最后是多次调用visitInnerClass、visitField和visitMethod方法。调用完后再调用visitEnd方法作为结尾。注意ClassWriter类,该类实现了ClassVisitor接口,经过toByteArray方法能够把已编译类直接建立成二进制形式。因为我们要动向生成子类,因此这里只对ClassWriter感兴趣。第一是抽象类原型:11/@authorrosenjiangMyAbsClassclass*/packageorg.rosenjiang.test;publicabstractclassMyAbsClass{intLESS=-1;intEQUAL=0;intGREATER=1;abstractintabsTo(Objecto);}其次是自定义类加载器,实在无法,ClassLoader的defineClass方法都是protected的,要加载字节数组形式(因为toByteArray了)的类只有继承一下自己再实现。/@authorrosenjiangMyClassLoaderclass*/packageorg.rosenjiang.test;publicclassMyClassLoaderextendsClassLoader{publicClassdefineClass(Stringname,byte[]b){returndefineClass(name,b,0,b.length);}}最后是测试类。/@authorrosenjiangOOMPermTestclass*/packageorg.rosenjiang.test;importjava.util.ArrayList;importjava.util.List;importorg.objectweb.asm.ClassWriter;importorg.objectweb.asm.Opcodes;publicclassOOMPermTest{publicstaticvoidmain(String[]args){OOMPermTesto=newOOMPermTest();o.oom();}privatevoidoom(){try{ClassWritercw=newClassWriter(0);cw.visit(Opcodes.V1_5,Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"org/rosenjiang/test/MyAbsClass",null,"java/lang/Object",newString[]{});cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,"LESS","I",null,newInteger(-1)).visitEnd();cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,"EQUAL","I",null,newInteger(0)).visitEnd();cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,"GREATER","I",null,newInteger(1)).visitEnd();cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"absTo",12"(Ljava/lang/Object;)I",null,null).visitEnd();cw.visitEnd();byte[]b=cw.toByteArray()

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论