内存泄漏的判断
判断系统是否存在内存泄漏的依据是: 如果系统存在内存泄漏, 那么完全垃圾回收完之后的内存值应该持续上升。 如果在现场能观察到这个现象, 说明系统存在内存泄漏。 当怀疑一个系统存在内存泄漏的时候, 首先使用FULL GC信息对内存泄漏进行一个初步确认, 确认系统是否存在内存泄漏。 只检查完全垃圾回收后的可用内存值是否一直再增大, 步骤如下:
- 首先截取系统稳定运行以后的GC信息
- 过滤出FULL GC的行。 只有FULL GC的行才有分析价值。 因为完全GC后的内存是当前Java对象真正使用的内存数量
如果完全垃圾回收后的内存持续增长, 大有一直增长到Xmx设定值的趋势, 那么这个时候基本上就可以断定系统存在内存泄漏。
如果当前完全垃圾回收后内存增长到一个值之后, 又能回落, 总体上处于一个动态平衡, 那么内存泄漏基本可以排除。
内存泄露的原因
直接内存
native区域内存泄露的原因可能有三:
- 如果系统中存在JNI调用, 本地内存泄漏可能存在于JNI代码中。
- JDK的Bug.
- 操作系统的Bug.
本地内存泄露也可能会引发OOM,但如果出现的是Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread这个异常,则往往是创建的线程过多或者堆内存过大导致的
主要原因可能有三:
- 系统当前有过多的线程, 操作系统无法再创建更多的线程
- swap分区不足
- 在32位的系统下, 过大的堆内存设置, 导致本地内存不足。32位操作系统下一个进程的内存空间是有限的,当设置的heap大小+perm大小比较大时,势必会挤压native的大小。解决方式:(a) 减少Xmx或者PermSize的设置 (b) 如果系统需要的堆内存确实很大, 无法减少Xmx的设置, 可以通过设置-Xss强行将每个线程堆栈的尺寸设小, 一旦线程堆栈过长, 则自动截断, 从而可以让线程堆栈占用的内存不过渡膨胀。 但这个效果往往有限的。
Perm
在使用大量反射、jsp、jar包、动态代理的情况下,加载的class很多,就有可能会OOM。
查看swap使用情况可以参见:Swap使用情况
内存泄漏的定位
- jmap -histo
> objhist.log, 可以打印出当前对象的个数和大小 - 如果系统已经OutOfMemory异常并停止工作, 可以通过jmap -heap:format=b
获取内存信息 - 在启动期间增加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=”具体的路径”, 当系统一旦OutOfMemory之后, 就会将内存信息和堆信息收集下来。