一、问题现状
现实企业级Java开发中,有时候我们会碰到下面这些问题:
OutOfMemoryError,内存不足内存泄露线程死锁锁争用(Lock Contention)Java进程消耗CPU过高...... 这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。本文将对一些常用的JVM性能调优监控工具hprof进行介绍。
二、hprof文件的生成
生成hprof文件可以在DDMS(DDMS的全称是DalvikDebug Monitor Service,是Android开发环境中的Dalvik虚拟机调试监控服务。提供测试设备截屏、查看特定进程正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、模拟接收及发送SMS、虚拟地理坐标等服务)选中进程点击窗口左上角的"dump hprof file"按钮来直接生成,也可以通过在程序加代码中来生成,以下通过设置生成hprof文件: 我们希望在memory 溢出时候能自动生成heap dump文件,为此,我们在运行时候添加JVM 参数:-XX:+HeapDumpOnOutOfMemoryError注:dump生成的.hprof文件默认放到了该项目目录下面。 先构造一个实体类User,这个User类就是一般的java 类,然后我们构造一个ArrayList,然后在一个无限循环中一直放这个User类的实例,因为User类和ArrayList都在堆上,而ArrayList因为是强引用,所以无法被GC回收,(因为我们List一直在用并没有摧毁)所以一旦ArrayList所占用的堆内存填满整个heap size时候,heap就溢出了。
[java]view plaincopypackagecom.deppon.tps; publicclassUser{ privateStringname; privateStringsex; privateintage; publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicStringgetSex(){ returnsex; } publicvoidsetSex(Stringsex){ this.sex=sex; } publicintgetAge(){ returnage; } publicvoidsetAge(intage){ this.age=age; } publicUser(Stringname,Stringsex,intage){ this.name=name; this.sex=sex; this.age=age; } } 然后我们创建一个main方法就是上面的Test1类里面,创建ArrayList,并且无限向其中添加User类对象的方法是:[java]view plaincopypackagecom.deppon.tps; importjava.util.ArrayList; importjava.util.List; publicclassTest1{ publicstaticvoidmain(String[]args){ List<User>persons=newArrayList<User>(); while(1>0){ persons.add(newUser("liuhai","male",25)); } } } 当运行上述代码时候,堆溢出了,产生了heap dump文件(这里用VM参数指定了如果堆溢出则产生heap dump)
用MAT(Memory Analyze Tool)工具--插件下载、安装及打开hprof文件见以下链接,分析这个hprof文件(java_pid39340.hprof),我们发现了,它的确探测出了memory leak问题,如下:
如图所示,这里很清楚的表示,main()方法中有一个集合类型,,然后集合中的每一个元素都是com.charles.research.User的对象,并且每个对象的Shallow Heap和Retained Heap大小都为24byte。因为ArrayList一直存在,所以当对象足够多时候,就把heap弄满并且溢出了。这里创建了76340681个User对象,每个对象占据了24个byte,所以一共占据了24 *7634068=183217632字节,差不多约为174.72M大小的堆空间。在这里User对象的Shallow Size和Retained Size都为24byte呢?
我们要看下Shallow Size和Retained Size各是什么?
Shallow Size是对象本身占据的内存的大小,不包含其引用的对象。对于常规对象(非数组)的Shallow Size由其成员变量的数量和类型来定,而数组的ShallowSize由数组类型和数组长度来决定,它为数组元素大小的总和。
Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C,C就是间接引用) ,并且排除被GC Roots直接或者间接引用的对象
所以,我们这里的User类,因为我们机器是32位 WIN7系统,所以对象头占据8byte,它包含String对象引用(name),占据4byte,包含String对象引用(sex),占据4byte,包含一个int类型(age),占据4byte,所以一共占据8+4+4+4=20byte, 因为要补齐位数,所以最后尺寸为24byte. 这就是这个对象的本身大小(Shallow Heap)的大小。
小知识:为了说明补齐,大家也可以做实验,如果我们User中加一个String成员,那么User类大小仍然为24byte,因为这个新String对象的引用4byte刚好去填了补齐的那个坑,如果再加一个String成员,那么User类大小就直接从24byte升到32byte了,因为又产生了一个坑需要补齐。
而我们的User类中没有引入其他的类(不包括String,因为String直接被Root GC引用),所以回收User占据的内存就是回收User自身,所以Retained Heap大小等同于Shallow Heap大小。
三、tomcat中设置jvm参数
linux系统中
1.打开/tomcat_home/bin/catalina.sh文件
2.加上:JAVA_OPTS="$JAVA_OPTS -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump"
如下图位置:
注:其中不设-XX:HeapDumpPath时,dump出的文件在/tomcat_home/bin目录下
Windows系统中
1.打开/tomcat_home/bin/catalina.bat文件
2.加上:set JAVA_OPTS=%JAVA_OPTS% -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump
如下图位置:
MAT下载:/mat/downloads.php MAT Memory Analyzer Tool 插件安装(图解) /articles/3yMN7z
如果觉得《JVM性能调优监控工具hprof使用详解》对你有帮助,请点赞、收藏,并留下你的观点哦!