目录
1、介绍
打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。
可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。使用方法 jmap -histo pid。如果连用SHELL jmap -histo pid>a.log可以将其保存到文本中去,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具(Memory Analysis Tool),使用参见:http://blog.csdn.net/fenglibing/archive/2011/04/02/6298326.aspx)或与jhat (Java Heap Analysis Tool)一起使用,能够以图像的形式直观的展示当前内存是否有问题。
查看该进程(pid)下堆内存的使用情况:
jmap -heap pid
快速定位内存泄漏的方法:只统计存活的对象
jmap -histo:live pid
还可以导出:
jmap -histo:live pid >1.txt将信息输出到指定文件中
2、命令格式
SYNOPSIS
jmap [ option ] pid
jmap [ option ] executable core
jmap [ option ] [server-id@]remote-hostname-or-IP
3、参数说明
1)、options:
executable Java executable from which the core dump was produced.
(可能是产生core dump的java可执行程序)
core 将被打印信息的core dump文件
remote-hostname-or-IP 远程debug服务的主机名或ip
server-id 唯一id,假如一台主机上多个远程debug服务
2)、基本参数:
-dump:[live,]format=b,file= 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.
-finalizerinfo 打印正等候回收的对象的信息.
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.
-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.
-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.
-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.
-h | -help 打印辅助信息
-J 传递参数给jmap启动的jvm.
pid 需要被打印配相信息的java进程id,创业与打工的区别 - 博文预览,可以用jps查问.
4、使用示例
tasklist命令查看进程id(Tasklist"是 winxp/win2003/vista/win7/win8下的命令,用来显示运行在本地或远程计算机上的所有进程,带有多个执行参数。)
常用的参数如下:
histo
jmap -histo pid 展示class的内存情况
展示的信息为编号,实例数,字节,类名
-finalizerinfo
打印等待回收的对象信息
jmap -finalizerinfo 4783Attaching to process ID4783, please wait...
Debugger attached successfully.
Server compiler detected.
JVM versionis 25.171-b11
Number of objects pendingfor finalization: 0[root@ip-172-29-206-104 applogs]#
heap
jmap -heap pid 展示pid的整体堆信息
jmap -heap 2464
JVM version is 16.3-b01
using thread-local object allocation.
Parallel GC with 13 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 8436842496 (8046.0MB)
NewSize = 5439488 (5.1875MB)
MaxNewSize = 17592186044415 MB
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 88080384 (84.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 87883776 (83.8125MB)
used = 31053080 (29.614524841308594MB)
free = 56830696 (54.197975158691406MB)
35.33425782706469% used
From Space:
capacity = 13828096 (13.1875MB)
used = 196608 (0.1875MB)
free = 13631488 (13.0MB)
1.4218009478672986% used
To Space:
capacity = 16384000 (15.625MB)
used = 0 (0.0MB)
free = 16384000 (15.625MB)
0.0% used
PS Old Generation
capacity = 156172288 (148.9375MB)
used = 27098208 (25.842864990234375MB)
free = 129074080 (123.09463500976562MB)
17.35148299805917% used
PS Perm Generation
capacity = 88080384 (84.0MB)
used = 50847592 (48.492042541503906MB)
free = 37232792 (35.507957458496094MB)
57.728622073218936% used
说明如下
Parallel GC with 13thread(s) #13个gc线程
Heap Configuration:#堆内存初始化配置
MinHeapFreeRatio= 40 #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率
MaxHeapFreeRatio= 70 #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率
MaxHeapSize= 8436842496 (8046.0MB)#-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize= 5439488 (5.1875MB) #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize= 17592186044415 MB #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize= 5439488 (5.1875MB) #-XX:OldSize=设置JVM堆的‘老生代’的大小
NewRatio= 2 #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio= 8 #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize= 21757952 (20.75MB) #-XX:PermSize=:设置JVM堆的‘永生代’的初始大小
MaxPermSize= 88080384 (84.0MB) #-XX:MaxPermSize=:设置JVM堆的‘永生代’的最大大小
Heap Usage:
PS Young Generation
Eden Space:#Eden区内存分布
capacity= 87883776 (83.8125MB)
used= 31053080 (29.614524841308594MB)
free= 56830696 (54.197975158691406MB)35.33425782706469% used
From Space:#其中一个Survivor区的内存分布
capacity= 13828096 (13.1875MB)
used= 196608 (0.1875MB)
free= 13631488 (13.0MB)1.4218009478672986% used
To Space:#另一个Survivor区的内存分布
capacity= 16384000 (15.625MB)
used= 0 (0.0MB)
free= 16384000 (15.625MB)0.0% used
PS Old Generation#当前的Old区内存分布
capacity= 156172288 (148.9375MB)
used= 27098208 (25.842864990234375MB)
free= 129074080 (123.09463500976562MB)17.35148299805917% used
PS Perm Generation#当前的 “永生代” 内存分布
capacity= 88080384 (84.0MB)
used= 50847592 (48.492042541503906MB)
free= 37232792 (35.507957458496094MB)57.728622073218936% used
mat为eclipse的一个内存分析插件,帮助查找内存泄漏和减少内存消耗。
首先基于jmap导出的堆信息
jmap -dump:live,format=b,file=test.bin 29030
准备代码:
classUser {privateString id;privateString name;publicString getId() {returnid;
}public voidsetId(String id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicUser(String id, String name) {super();this.id =id;this.name =name;
}
}
main方法:
public static voidmain(String[] args) {
List list = new ArrayList();for (int i = 1; i < 10000; i++) {
User o= new User(i + "", System.currentTimeMillis() + "");
list.add(o);
o= null;
}
System.out.println("end");try{
Thread.sleep(100000000l);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
执行之后用jmap输出堆信息
然后导入分析工具
我们可以看到图形化展示:
然后我们点击
Problem Suspect 1
如下所示:
然后点击详情
我们可以看到有很多的User对象
这些对象有可能会溢出,然后我们打开OQL窗口看他是否为null,执行如下OQL语句
SELECT u FROM org.learn.util.User u WHERE (u.value = null)
结果如下:
也就是说这个是null,但是仍然有强引用存在,gc的时候是不能回收的,这样就会出现内存的溢出问题
示例2:如何用mat分析内存问题
我用MAT打开了heap.bin,很容易看出,char[]的数量出其意料的多,占用90%以上的内存 。一般来说,char[]在JVM确实会占用很多内存,数量也非常多,因为String对象以char[]作为内部存储。但是这次的char[]太贪婪 了,仔细一观察,发现有数万计的char[],每个都占用数百K的内存 。这个现象说明,Java程序保存了数以万计的大String对象 。结合程序的逻辑,这个是不应该的,肯定在某个地方出了问题。
在可疑的char[]中,任意挑了一个,使用Path To GC Root功能,找到该char[]的引用路径,发现String对象是被一个HashMap中引用的 。这个也是意料中的事情,Java的内存泄露多半是因为对象被遗留在全局的HashMap中得不到释放。不过,该HashMap被用作一个缓存,设置了缓 存条目的阈值,导达到阈值后会自动淘汰。从这个逻辑分析,应该不会出现内存泄露的。虽然缓存中的String对象已经达到数万计,但仍然没有达到预先设置 的阈值(阈值设置地比较大,因为当时预估String对象都比较小)。
但是,另一个问题引起了我的注意:为什么缓存的String对象如此巨大?内部char[]的长度达数百K。虽然缓存中的 String对象数量还没有达到阈值,但是String对象大小远远超出了我们的预期,最终导致内存被大量消耗,形成内存泄露的迹象(准确说应该是内存消 耗过多) 。
就这个问题进一步顺藤摸瓜,看看String大对象是如何被放到HashMap中的。通过查看程序的源代码,我发现,确实有String大对象,不 过并没有把String大对象放到HashMap中,而是把String大对象进行split(调用String.split方法),然后将split出 来的String小对象放到HashMap中 了。
这就奇怪了,放到HashMap中明明是split之后的String小对象,怎么会占用那么大空间呢?难道是String类的split方法有问题?