为了判断 Java 中是否有内存泄漏,我们首先必须了解 Java 是如何管理内存的。下面我们先给出一个简单的内存泄漏的例子,在这个例子中我们循环申请 Object 对象,并将所申请的对象放入一个 HashMap 中,如果我们仅仅释放引用本身,那么 HashMap 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。
HashMap mapObj = new HashMap() public void myfun() { String obj1 = new String("abcd"); mapObj.put(obj1, obj1); ... obj1 = null; //此时 obj1 指向的物理内存没有释放,因为 hashmap 引用该地址 }
JVM 可以自动回收垃圾,但它只能回收满足条件的垃圾,有时需要们确保条件的满足。如果程序中,存在越来越多不在影响程序未来执行的对象(也就是不再需要的对象),而且这些对象和根对象之间存在引用路径,那么就发生了内存泄漏。
内存泄漏常发生在如下场景:
在 64 位的平台上,Java 对象的占用内存如下
类型 | 大小 |
---|---|
Object | 16 |
Float | 16 |
Double | 24 |
Integer | 16 |
Long | 24 |
为了说明对象和引用,我们先定义一个简单的类
class Person { String name; int age; }
Person p1 = new Person() 包含如下几个动作
如果再将对象赋值给 p2 的话,变成下面这样的
执行 p2 = new Person() 之后变成
垃圾自动回收做两件事情:
标记过程现在主要使用 根可达性 分析(还有引用计数法等),清除之后可能会有一些小的内存快,所有还有压缩的过程。
下图中的灰色对象表示可以被回收的对象(根不可达)
哪些对象可以成为 根 呢? http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
内存不足会有三种情况:
发生 OOM 的时候,可以检查如下几个方面:
内存泄漏一般会有如下几个症状:
内存泄漏的分析并不复杂,但需要耐心,一般内存泄漏只能事后分析,而重现问题需要耐心。
当出现 java.lang.OutOfMemoryError: Java Heap Space 异常,就表示堆内存不足了。堆内存不足的原因有如下几种:
8190.813: [GC 164675K->251016K(1277056K), 0.0117749 secs] #8190.813 表示垃圾回收的时间点,秒为单位。GC/Full GC 表示垃圾回收的类型 8190.825: [Full GC 251016K->164654K(1277056K), 0.8142190 secs] # 251016K表示回收前占用的内存大小,164654K 表示回收后占用的内存大小,1277056K 表示当前对内存总大小,0.8142190 表示回收耗时 8191.644: [GC 164678K->251214K(1277248K), 0.0123627 secs] 8191.657: [Full GC 251214K->164661K(1277248K), 0.8135393 secs] 8192.478: [GC 164700K->251285K(1277376K), 0.0130357 secs] 8192.491: [Full GC 251285K->164670K(1277376K), 0.8118171 secs] 8193.311: [GC 164726K->251182K(1277568K), 0.0121369 secs] 8193.323 : [Full GC 251182K->164644K(1277568K), 0.8186925 secs] 8194.156: [GC 164766K->251028K(1277760K), 0.0123415 secs] 8194.169: [Full GC 251028K->164660K(1277760K), 0.8144430 secs]
怀疑内存泄漏后,我们通过 Full GC 日志进一步确认,检查 Full GC 后的可用内存是否持续增大。步骤如下:
GC 日志只能帮忙找到是否有泄漏,找出内存泄漏的地方,需要依赖一些其他的工具
GC 日志无异常,但 Java 进程使用内存逐渐增大,并且无停止上涨的趋势。本地内存泄漏的原因有如下几个:
本地内存泄漏可能伴有如下异常
java.lang.OutOfMemoryError: unable to create new native thread , Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:574) at TestThread.main(TestThread.java:34)
上面这个异常可能的原因有:
出现 java.lang.OutOfMemoryError: PermGen space Perm ,说明 Perm 区内存不足