请教java堆外java 内存泄漏定位分析定位方法

记住登录一个月发表随想还能输入:200字摘要:堆外内存用一般的工具很难查看,可以通过google-perftools来跟踪:。它的原理是在java应用程序运行时,当调用malloc时换用它的libtcmalloc.so,这样就能做一些统计了。堆外内存用一般的工具很难查看,可以通过google-perftools来跟踪:
它的原理是在java应用程序运行时,当调用malloc时换用它的libtcmalloc.so,这样就能做一些统计了
下载,sudo make install
下载, configure --prefix=/home/user/sudo make install
在应用程序启动前加入:export LD_PRELOAD=/home/hadoop/perftools/lib/libtcmalloc.so以及export HEAPPROFILE=/home/user/perftools/test
修改lc_config:sudo vi /etc/ld.so.conf.d/usr_local_lib.conf,加入/usr/local/lib(libunwind的lib所在目录)
执行sudo /sbin/ldconfig,使libunwind生效
启动应用程序,此时会在/home/user/perftools/下看到诸如test_pid.xxxx.heap的heap文件,可使用bin/pprof --text $JAVA_HOME/bin/java test_pid.xxxx.heap来查看
分享到:更多昵称: 小蜜锋等级: 高级设计师积分: 8260代码: 747 个文章: 353 篇随想: 210 条访问: 790 次人气文章人气代码标签最新提问站长推荐29692人阅读
服务器性能监控(56)
jvm性能调优(23)
<span style="color:#、为什么会发生内存泄漏
java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题。
编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到堆内存不够用时,jvm会进行垃圾回收,清除这些对象占用的堆内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时,无法从Heap中获取足够的内存分配给对象,这时候就会导致内存溢出。而出现内存泄露的地方,一般是不断的往容器中存放对象,而容器没有相应的大小限制或清除机制。容易导致内存溢出。
当服务器应用占用了过多内存的时候,如何快速定位问题呢?现在,Eclipse MAT的出现使这个问题变得非常简单。EclipseMAT是著名的SAP公司贡献的一个工具,可以在Eclipse网站下载到它,完全免费的。
&&& 要定位问题,首先你需要获取服务器jvm某刻内存快照。jdk自带的jmap可以获取内存某一时刻的快照,导出为dmp文件后,就可以用Eclipse
MAT来分析了,找出是那个对象使用内存过多。
<span style="color:#、内存泄漏的现象:
常常地,程序内存泄漏的最初迹象发生在出错之后,在你的程序中得到一个OutOfMemoryError。这种典型的情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中暴露。这种情况下,你需要一个低负荷的工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。
  一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。
<span style="color:#、发现内存泄漏
&& 1. jstat -gc pid
&&&&&&&&&&&可以显示gc的信息,查看gc的次数,及时间。
&&&&&&&&&&&其中最后五项,分别是young gc的次数,young gc的时间,full
gc的次数,full gc的时间,gc的总时间。
&&&&&2.jstat -gccapacity pid
&&&&&&&&&&&可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,
&&&&&&&&&&&如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,
&&&&&&&&&&&PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。
&&&&&&&&&&&其他的可以根据这个类推, OC是old内纯的占用量。
&&&& 3.jstat -gcutil pid
&&&&&&&&&&&&统计gc信息统计。
&&&& 4.jstat -gcnew pid
&&&&&&&&&& &年轻代对象的信息。
&&&& 5.jstat -gcnewcapacity pid
&&&&&&&&&&&年轻代对象的信息及其占用量。
&&&& 6.jstat -gcold pid
&&&&&&&&& &&old代对象的信息。
&&&& 7.stat -gcoldcapacity pid
&&&&&&&&& &old代对象的信息及其占用量。
&&&& 8.jstat -gcpermcapacity pid
&&&&&&&&& &perm对象的信息及其占用量。
&&&& 9.jstat -class pid
&&&&&&& && 显示加载class的数量,及所占空间等信息。
&&&& 10.jstat -compiler pid
&&&&&&&&& &显示VM实时编译的数量等信息。
&&&& 11.stat -printcompilation pid
&&&&&&&&& 当前VM执行的信息。
&&&&&&& 一些术语的中文解释:
&&&&&&&& S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
&&&&&&&& S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
&&&&&&&& S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
&&&&&&&& S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
&&&&&&&&&&EC:年轻代中Eden(伊甸园)的容量 (字节)
&&&&&&&&&&EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
&&&&&&&&&&OC:Old代的容量 (字节)
&&&&&&&&&&OU:Old代目前已使用空间 (字节)
&&&&&&&&&&PC:Perm(持久代)的容量 (字节)
&&&&&&&&&&PU:Perm(持久代)目前已使用空间 (字节)
&&&&&&&& YGC:从应用程序启动到采样时年轻代中gc次数
&&&&&& &YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
&&&&&&&& FGC:从应用程序启动到采样时old代(全gc)gc次数
&&&&&& &FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
&&&&&&&& GCT:从应用程序启动到采样时gc用的总时间(s)
&&&&& &NGCMN:年轻代(young)中初始化(最小)的大小
&&& &&&NGCMX:年轻代(young)的最大容量 (字节)
&&&&&&& &NGC:年轻代(young)中当前的容量 (字节)
&& &&&&OGCMN:old代中初始化(最小)的大小
&&& &&&OGCMX:old代的最大容量 (字节)
&&&&&& &OGC:old代当前新生成的容量 (字节)
&& &&&&PGCMN:perm代中初始化(最小)的大小
&& &&&&PGCMX:perm代的最大容量 (字节)&&&
&&&&&& &&PGC:perm代当前新生成的容量 (字节)
&&&&& & &S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
&&&&&&&& &S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
& & & &&&E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
& & & & &O:old代已使用的占当前容量百分比
& & & && P:perm代已使用的占当前容量百分比
& &&&&&S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)
&&&&& &S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节)
&&&&& &&ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)
&&&&&& &&DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满)
&&&&&&& & TT:持有次数限制
&&&& & &&MTT :最大持有次数限制
如果定位内存泄漏问题我一般使用如下命令:
Jstat &-gcutil 70
[root@ssss logs]# jstat -gcutil 1 300
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 1.46 26.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 46.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 47.04 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 65.19 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 67.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 87.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 88.03 4.61 30.14 35 0.872 0 0.000 0.872
1.48 0.00 5.56 4.62 30.14 36 0.874 0 0.000 0.874
1000 代表多久间隔显示一次,
100 代表显示一次。
S0 — Heap上的 Survivor space 0 区已使用空间的百分比
S1 — Heap上的 Survivor space 1 区已使用空间的百分比
E — Heap上的 Eden space 区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
如果有大量的FGC就要查询是否有内存泄漏的问题了,图中的FGC数量就比较大,并且执行时间较长,这样就会导致系统的响应时间较长,如果对jvm的内存设置较大,那么执行一次FGC的时间可能会更长。
如果为了更好的证明FGC对服务器性能的影响,我们可以使用java visualVM来查看一下:
从上图可以发现执行FGC的情况,下午<span style="color:#:10分之前是没有FGC的,之后出现大量的FGC。
上图是jvm堆内存的使用情况,下午<span style="color:#:10分之前的内存回收还是比较合理,但是之后大量内存无法回收,最后导致内存越来越少,导致大量的full
下面我们在看看大量full GC对服务器性能的影响,下面是我用loadrunner对我们项目进行压力测试相应时间的截图:
从图中可以发现有,在进行full GC后系统的相应时间有了明显的增加,点击率和吞吐量也有了明显的下降。所以java内存泄漏对系统性能的影响是不可忽视的。
<span style="color:#、定位内存泄漏
当然通过上面几种方法我们可以发现java的内存泄漏问题,但是作为一名合&#26684;的高级工程师,肯定不甘心就把这样的结论交给开发,当然这也的结论交给开发,开发也很难定位问题,为了更好的提供自己在公司的地位,我们必须给开发工程师提供更深入的测试结论,下面就来认识一下MemoryAnalyzer.exe。java内存泄漏检查工具利器。
首先我们必须对jvm的堆内存进行dump,只有拿到这个文件我们才能分析出jvm堆内存中到底存了些什么内容,到底在做什么?
MemoryAnalyzer的用户我在这里就不一一说明了,我的博客里也有说明,下面就展示我测试的成功图:
其中深蓝色的部分就为内存泄漏的部分,java的堆内存一共只有<span style="color:#1.5M而内存泄漏的部分独自占有了<span style="color:#6.2M所以本次的内存泄漏很明显,那么我就来看看那个方法导致的内存泄漏:
从上图我们可以发现红线圈着的方法占用了堆内存的<span style="color:#.75%,如果能把这个测试结果交给开发,开发是不是应该很好定位呢。所以作为一名高级测试工程师,我们需要学习的东西太多。
虽然不确定一定是内存泄漏,但是可以准确的告诉开发问题出现的原因,有一定的说服力。
本人刚刚完成了云存储架构师的培训学习(包括了linux的内核了解、 shell的高级编程、linux安全的学习重点iptables和tcp/ip等各种协议的抓包分析、linux的集群、性能调优等接下来还有dba的课程等待着我挑战)。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1989268次
积分:21790
积分:21790
排名:第248名
原创:297篇
转载:328篇
评论:228条
1.凤凰网:(性能测试)
2.猎聘网:(性能+接口自动化)
3.奇虎360(web平台*服务端测试):(服务的性能、手机浏览器性能、自动化)
(1)(1)(1)(1)(4)(2)(3)(7)(2)(2)(2)(2)(1)(2)(2)(1)(4)(2)(2)(3)(12)(5)(6)(7)(21)(12)(21)(30)(27)(17)(36)(29)(34)(17)(50)(25)(14)(18)(13)(33)(26)(10)(30)(65)(26)(1)送173元,399311.con官方网址
淮阴宠物网
淮阴宠物网,同城宠友交流,宠物爱好者交流平台!
淮阴宠物网3930人阅读
java高级话题(17)
JVM可以使用的内存分外2种:堆内存和堆外内存,堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemoryError这个错误。使用堆外内存,就是为了能直接分配和释放内存,提高效率。JDK5.0之后,代码中能直接操作本地内存的方式有2种:使用未公开的Unsafe和NIO包下ByteBuffer。
关于Unsafe对象的简介和获取方式,可以参考我的另一篇博客 &
使用ByteBuffer分配本地内存则非常简单,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)即可。
C语言的内存分配和释放函数malloc/free,必须要一一对应,否则就会出现内存泄露或者是野指针的非法访问。java中我们需要手动释放获取的堆外内存吗?
1、首先我们看下NIO中提供的ByteBuffer
import java.nio.ByteB
public class TestDirectByteBuffer
// -verbose:gc -XX:+PrintGCDetails -XX:MaxDirectMemorySize=40M
public static void main(String[] args) throws Exception
while (true)
ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);
我们将最大堆外内存设置成40M,运行这段代码会发现:程序可以一直运行下去,不会报OutOfMemoryError。如果使用了-verbose:gc -XX:&#43;PrintGCDetails,会发现程序频繁的进行垃圾回收活动。于是我们可以得出结论:ByteBuffer.allocateDirect分配的堆外内存不需要我们手动释放,而且ByteBuffer中也没有提供手动释放的API。也即是说,使用ByteBuffer不用担心堆外内存的释放问题,除非堆内存中的ByteBuffer对象由于错误编码而出现内存泄露。
2、接下来我们看下直接在方法体中使用Unsafe的效果
import sun.misc.U
public class TestUnsafeMemo
// -XX:MaxDirectMemorySize=40M
public static void main(String[] args) throws Exception
Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
while (true)
long pointer = unsafe.allocateMemory(1024 * 1024 * 20);
System.out.println(unsafe.getByte(pointer + 1));
// 如果不释放内存,运行一段时间会报错java.lang.OutOfMemoryError
// unsafe.freeMemory(pointer);
}这段程序会报OutOfMemoryError错误,也就是说allocateMemory和freeMemory,相当于C语音中的malloc和free,必须手动释放分配的内存。
3、类&#20284;于ByteBuffer,将Unsafe分配内存封装到一个类中
import sun.misc.U
public class ObjectInHeap
private long address = 0;
private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
public ObjectInHeap()
address = unsafe.allocateMemory(2 * 1024 * 1024);
// Exception in thread &main& java.lang.OutOfMemoryError
public static void main(String[] args)
while (true)
ObjectInHeap heap = new ObjectInHeap();
System.out.println(&memory address=& + heap.address);
这段代码会抛出OutOfMemoryError。这是因为ObjectInHeap对象是在堆内存中分配的,当该对象被垃圾回收的时候,并不会释放堆外内存,因为使用Unsafe获取的堆外内存,必须由程序显示的释放,JVM不会帮助我们做这件事情。由此可见,使用Unsafe是有风险的,很容易导致内存泄露。
4、正确释放Unsafe分配的堆外内存
& & & & 虽然第3种情况的ObjectInHeap存在内存泄露,但是这个类的设计是合理的,它很好的封装了直接内存,这个类的调用者感受不到直接内存的存在。那怎么解决ObjectInHeap中的内存泄露问题呢?可以覆写Object.finalize(),当堆中的对象即将被垃圾回收器释放的时候,会调用该对象的finalize。由于JVM只会帮助我们管理内存资源,不会帮助我们管理数据库连接,文件句柄等资源,所以我们需要在finalize自己释放资源。
import sun.misc.U
public class RevisedObjectInHeap
private long address = 0;
private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();
// 让对象占用堆内存,触发[Full GC
private byte[] bytes =
public RevisedObjectInHeap()
address = unsafe.allocateMemory(2 * 1024 * 1024);
bytes = new byte[1024 * 1024];
protected void finalize() throws Throwable
super.finalize();
System.out.println(&finalize.& + bytes.length);
unsafe.freeMemory(address);
public static void main(String[] args)
while (true)
RevisedObjectInHeap heap = new RevisedObjectInHeap();
System.out.println(&memory address=& + heap.address);
我们覆盖了finalize方法,手动释放分配的堆外内存。如果堆中的对象被回收,那么相应的也会释放占用的堆外内存。这里有一点需要注意下:
// 让对象占用堆内存,触发[Full GC
private byte[] bytes =
这行代码主要目的是为了触发堆内存的垃圾回收行为,顺带执行对象的finalize释放堆外内存。如果没有这行代码或者是分配的字节数组比较小,程序运行一段时间后还是会报OutOfMemoryError。这是因为每当创建1个RevisedObjectInHeap对象的时候,占用的堆内存很小(就几十个字节左右),但是却需要占用2M的堆外内存。这样堆内存还很充足(这种情况下不会执行堆内存的垃圾回收),但是堆外内存已经不足,所以就不会报OutOfMemoryError。
虽然改进后的RevisedObjectInHeap不会有堆外内存泄露,但是这种解决方法却无端地浪费了堆内存。简单的看了下ByteBuffer的源码,它内部分配堆外内存也是通过unsafe.allocateMemory()实现的。那ByteBuffer又是怎么实现的堆外内存释放呢?难道也是通过第4种类&#20284;RevisedObjectInHeap的做法吗?欢迎大神指点迷津啊!
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:384350次
积分:6874
积分:6874
排名:第2537名
原创:304篇
评论:146条
(3)(8)(2)(2)(1)(9)(7)(1)(7)(5)(22)(17)(14)(16)(2)(3)(9)(10)(6)(13)(9)(16)(3)(16)(17)(29)(9)(10)(3)(7)(19)(9)(1)(3)(1)(2)对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收;而使用的内存是由JVM控制的。
那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM给的内存不够用,怎么办?
此时,堆外内存登场!利用堆外内存,不仅可以随意操控内存,还能提高网络交互的速度。
背景1:JVM内存的分配
  对于JVM的内存规则,应该是老生常谈的东西了,这里我就简单的说下:
  新生代:一般来说新创建的对象都分配在这里。
  年老代:经过几次垃圾回收,新生代的对象就会放在年老代里面。年老代中的对象保存的时间更久。
  永久代:这里面存放的是class相关的信息,一般是不会进行垃圾回收的。
背景2:JVM垃圾回收
  由于JVM会替我们执行垃圾回收,因此开发者根本不需要关心对象的释放。但是如果不了解其中的原委,很容易内存泄漏,只能两眼望天了!
  垃圾回收,大致可以分为下面几种:
  Minor GC:当新创建对象,内存空间不够的时候,就会执行这个垃圾回收。由于执行最频繁,因此一般采用复制回收机制。
  Major GC:清理年老代的内存,这里一般采用的是标记清除+标记整理机制。
  Full GC:有的说与Major GC差不多,有的说相当于执行minor+major回收,那么我们暂且可以认为Full GC就是全面的垃圾回收吧。
堆外内存?
  堆外内存,其实就是不受JVM控制的内存。相比于堆内内存有几个优势:
  1 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到)
  2 加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。
  而福之祸所依,自然也有不好的一面:
  1 堆外内存难以控制,如果内存泄漏,那么很难排查
  2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。
  堆外内存可以通过java.nio的ByteBuffer来创建,调用allocateDirect方法申请即可。
  至于怎么用,读读API文档就知道啦~  
  另外,默认的情况下堆外内存是有一定的限制的,好像是64M吧....
  可以通过设置-XX:MaxDirectMemorySize=10M控制堆外内存的大小:
堆外内存的垃圾回收
  堆外内存,既然可以无限使用,那么会不会用爆内存呢?这个是很有可能的...所以堆外内存的垃圾回收也很重要。
  由于堆外内存并不直接控制于JVM,因此只能等到full GC的时候才能垃圾回收!
  Full GC,一般发生在年老代垃圾回收以及调用System.gc的时候,这样肯定不能满足我们的需求!于是度娘帮助解决了这个问题,网上有朋友十分聪明的利用内部实现接口,反向获取到了一个clear方法!
package xing.
import java.nio.ByteB
import sun.nio.ch.DirectB
public class NonHeapTest {
public static void clean(final ByteBuffer byteBuffer) {
if (byteBuffer.isDirect()) {
((DirectBuffer)byteBuffer).cleaner().clean();
public static void sleep(long i) {
Thread.sleep(i);
}catch(Exception e) {
public static void main(String []args) throws Exception {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 200);
System.out.println("start");
sleep(5000);
clean(buffer);//执行垃圾回收
System.gc();//执行Full gc进行垃圾回收
System.out.println("end");
sleep(5000);
  这样就能手动的控制回收堆外内存了!其中sun.nio其实是java.nio的内部实现。所以你可能不能通过eclipse的自动排错找到这个包,直接复制
import sun.nio.ch.DirectB
  就行。
  由于本文整理与网络各种资料,有些不对的地方还请指正,共同探讨!
阅读(...) 评论()}

我要回帖

更多关于 ios 内存泄漏定位 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信