java 内存泄露 jmap_五、jdk工具之jmap(java memory map)、 mat之四--结合mat对内存泄露的分析、jhat之二--结合jmap生成的dump结果在浏览器上展示...

目录

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下的命令,用来显示运行在本地或远程计算机上的所有进程,带有多个执行参数。)

5d682fe80fa56e18bc8cf95956554ef0.png

常用的参数如下:

histo

jmap -histo pid 展示class的内存情况

展示的信息为编号,实例数,字节,类名

wGccRoAsLq0lwAAAABJRU5ErkJggg==

-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的整体堆信息

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

说明如下

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输出堆信息

然后导入分析工具

我们可以看到图形化展示:

1bdd5d8507fca1c2907a14d05f1e5e0c.png

然后我们点击

Problem Suspect 1

如下所示:

06cf72a39983afb6c24f42b80b00a6f5.png

然后点击详情

6a8d915bab18c94e70e88fbc5dc76fe8.png

我们可以看到有很多的User对象

c9f4c168964ab4bd8502b56f6246dd18.png

这些对象有可能会溢出,然后我们打开OQL窗口看他是否为null,执行如下OQL语句

SELECT u FROM org.learn.util.User u WHERE (u.value = null)

结果如下:

b85e38ac4da9fa3265efe11d0b4395b3.png

也就是说这个是null,但是仍然有强引用存在,gc的时候是不能回收的,这样就会出现内存的溢出问题

示例2:如何用mat分析内存问题

我用MAT打开了heap.bin,很容易看出,char[]的数量出其意料的多,占用90%以上的内存 。一般来说,char[]在JVM确实会占用很多内存,数量也非常多,因为String对象以char[]作为内部存储。但是这次的char[]太贪婪 了,仔细一观察,发现有数万计的char[],每个都占用数百K的内存 。这个现象说明,Java程序保存了数以万计的大String对象 。结合程序的逻辑,这个是不应该的,肯定在某个地方出了问题。

943d02c410ab655daf0fdf590534ccb1.png

在可疑的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方法有问题?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值