为了判断 Java 中是否有内存泄漏,我们首先必须了解 Java 是如何管理内存的。下面我们先给出一个简单的内存泄漏的例子,在这个例子中我们循环申请 Object 对象,并将所申请的对象放入一个 HashMap 中,如果我们仅仅释放引用本身,那么 HashMap 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。
JVM 可以自动回收垃圾,但它只能回收满足条件的垃圾,有时需要们确保条件的满足。如果程序中,存在越来越多不在影响程序未来执行的对象(也就是不再需要的对象),而且这些对象和根对象之间存在引用路径,那么就发生了内存泄漏。
内存泄漏常发生在如下场景:
在 64 位的平台上,Java 对象的占用内存如下
1.1对象及其引用
为了说明对象和引用,我们先定义一个简单的类
Person p1 = new Person() 包含如下几个动作
1、右边的 new Person 在堆空间分配一块内存,创建一个 Person 类对象
2、末尾的 () 意味着创建对象之后,立即调用构造函数,进行初始化
3、左边的 Person p1 创建了一个引用变量,所谓引用变量,就是后来用于指向 Person 类示例的引用
4、= 符号使刚刚创建的对象引用指向刚刚创建的对象
上面的代码如下所示:
如果再将对象赋值给 p2 的话,变成下面这样的
执行 p2 = new Person() 之后变成
垃圾自动回收做两件事情:
1、标记垃圾
2、清除垃圾
标记过程现在主要使用 根可达性 分析(还有引用计数法等),清除之后可能会有一些小的内存快,所有还有压缩的过程。
下图中的灰色对象表示可以被回收的对象(根不可达)
哪些对象可以成为根呢? http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
2.1 为什么会发生 OOM 问题?
内存不足会有三种情况:
发生 OOM 的时候,可以检查如下几个方面:
内存泄漏一般会有如下几个症状:
内存泄漏的分析并不复杂,但需要耐心,一般内存泄漏只能事后分析,而重现问题需要耐心。
当出现 java.lang.OutOfMemoryError: Java Heap Space 异常,就表示堆内存不足了。堆内存不足的原因有如下几种:
怀疑内存泄漏后,我们通过 Full GC 日志进一步确认,检查 Full GC 后的可用内存是否持续增大。步骤如下:
GC 日志无异常,但 Java 进程使用内存逐渐增大,并且无停止上涨的趋势。本地内存泄漏的原因有如下几个:
本地内存泄漏可能伴有如下异常
上面这个异常可能的原因有:
出现 java.lang.OutOfMemoryError: PermGen space Perm ,说明 Perm 区内存不足