java系统性能优化(五)- 分析内存泄漏

前面说到几种代码问题导致cpu的us使用过高的情况,其中最不容易查找原因的就是内存泄漏问题了,而且内存泄漏导致的后果可能直接是程序挂掉无法使用,轻微的内存泄漏又往往需要很久的时间才会重现,而内存泄漏导致heap内存接近最大值时频繁gc又会导致cpu占用高。下面是我的一些分析内存泄漏的经验。

分析内存泄漏有很多种方法,比较通用的一种是出现内存溢出后将当前jvm内存给dump出来,也就是在启动参数上加上-XX:+
HeapDumpOnOutOfMemoryError来获得OOM时的Java Heap中的信息,当出现OOM时,会自动生成一个java_pid[pid].hprof 的文件,再用一些分析工具去分析这个dump文件,如果某个对象特别多,那么就可以确定是这个对象泄漏了。

但是,单纯靠这一个dump文件往往不太容易查处问题的原因,往往需要在程序运行过程中经过多次dump,通过jstat,jmap等工具,多次比较分析才能得出结论。这个时候使用专门的图形化prof工具更容易发现问题,比如sun jdk(现在是oracle)自带的jvisualvm,还有bea jrockit jdk(现在也是oracle…)的java mission controll,还有商业的jprofiler,现在简单介绍以下jproflier分析内存泄漏的方法。

首先,必须由jprofiler来引导程序启动,如果是在客户端远程监控服务端的话,必须在客户端和服务端都安装jprofiler,然后服务端需要在环境变量里加入LD_LIBRARY_PATH 值为JProfiler 的库文件所在路径,比如 $JPROFILER_HOME/bin/linux-x86 ,然后将服务端的启动脚本考到客户端上,在客户端配置时有一步选择这个脚本(locate the start script),jprofiler会给脚本添加一些自己的配置,然后服务端使用jprofiler改好的这个脚本启动,这时候是不会真正启动的,他在等待客户端的触发,客户端jprofiler再启动的时候就可以远程监控到服务端jvm了。本地的程序的话按照向导就很容易做了。

现在,我写一个有内存泄漏的程序来演示一下分析的过程。

压力测试一段时间后,可以先看一下VM Telemetry Views里的图形情况,如果每次垃圾回收后都会回到一个点的附近,并且长期来看整体图形没有增长趋势的话可以初步断定没有很明显的内存泄漏。
可能没有内存泄漏:

可能有轻微的内存泄漏:

但是如果是轻微的内存泄漏,光靠这个监控是找不出原因来的,需要对一些比较容易出现泄漏问题的类个数进行记录,可以在Memory Views这个页面右键点击比较有可能出现泄漏的类,然后add selection to class tracker。有几项最常出现泄漏的最好加进来:String,char[],HashMap的entry,以及通过下方的过滤器通过包名过滤出自己的项目里用到的类,尤其是个数比较多的一些。

再过一段时间后,查看memeory views里的class tracker的tab页,可以看到对象数量在这一段时间内的记录,如果持续增长那么就是这个类泄漏了。

定位了这个类就好办了,再就看一下是谁引用他导致内存没有释放,在heap walker里,找到刚才的class,右键它查看他的引用references:

然后就可以看到他所有的引用了,可以看到这个对象首先是由一个对象数组引用的,再点击对象数组左边的加号可以看到这个对象数组其实是ArrayList的一部分:

再继续看他的引用,就可以看到根源了:

原来是Process这个类里的一个arraylist引用了People类没有释放而导致的内存泄漏,再查看这个类的源代码可以看到:

public class Process {
List list = new ArrayList ();

public void test() {
People p = new People();
p.setAge(50);
list.add(p);
}

public void core(){
while (true) {
this.test();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

程序不断的给list添加people确没有删除掉,导致内存泄漏。

这是一个很简单的例子,实际系统中出现的内存泄漏原因可能要比这个复杂的很多,但往往都是一些细节没有注意到而导致的,比如缓冲队列中的数据不断增加确没有设置缓冲队列的最大值,threadlocal用完没有清空,hashmap保存不断增加的缓存数据没有clear等,但是通过jprofiler的这种分析方法都可以比较容易的定位问题。

一个原因比较复杂的内存泄漏查找可以看我之前的一个案例,当时是用的java mission control,不过原理都是一样的:
一次性能调优实战记录

1 Comments.

Leave a comment
  1. I drop a leave a response when I like a article on a blog or if I have something to valuable to contribute to the discussion. Usually it’s triggered by the passion communicated in the post I read. And on this post java蝟餌??扯隡?嚗?嚗? ????瘜? | beralee???? I was excited enough to post a commenta response ;-) I do have a couple of questions for you if you don’t mind. Is it just me or do a few of the responses appear as if they are left by brain dead individuals? :-P And, if you are posting at other social sites, I would like to keep up with anything new you have to post. Could you list every one of your shared pages like your Facebook page, twitter feed, or linkedin profile?

Leave a Reply

( Ctrl + Enter )