内存泄露简介

jopen 10年前

Java的最显著的优点中包含了一点,内存管理。 你只需要创建对象,Java的垃圾回收机制会负责分配和释放内存。然而实际情况并没有那么简单,因为在Java应用中会发生内存泄露。 这个帖子简要的说明了什么是内存泄露,它为什么会发生,怎样防止它发生。

1. 什么是内存泄露?

内存泄露: 对象不再被使用,但是又有引用指向它,所以不能被GC回收。

为了更清楚的理解这个定义,我们需要知道对象在内存中的状态。下面这个插图将对象分为两种状态,被引用的和未被引用的。其中被引用的对象中有一部分是没被使用的。内存泄露就发生在这些对象所在的内存区域。

内存泄露简介

 

2. 为什么会发生内存泄露?

让我们来看接下来这个例子,它会告诉我们为什么会发生内存泄露。在这个例子中,对象A指向对象B。对象A的生命周期是(t1-t4)比对象B的生命周期(t2-t3)长很多。当对象B不再被程序所使用的时候,对象A依然指向它。这样的话GC就不能从内存中回收对象B。这样的话就可能会引起内存溢出。因为如果对象A指向很多个这样的对象,内存中就会存在很多不能被回收内存空间的对象。

还有另外一种情况,B指向了其它的很多对象,导致了其它的对象也不能被回收。

内存泄露简介

3.怎样预防内存泄露?

以下有几点预防内存泄露的小建议:

  1. 留心使用集合类,如:HashMap,ArrayList,因为内存泄露通常是它们引起的。当它们被声明为 static,它们的生命周期就跟应用的生命周期相同。

  2. 留心使用事件监听和回调函数。如果监听被注册了之后但是该类不再被使用的时候没有注销也会引起内存泄露。

  3. 成员变量如果是对象的话,需要使用null来销毁这个对象的引用。

4. 为什么JDK6中的substring()方法会导致内存泄露?

JDK6中subString()的源码

//JDK 6  String(int offset, int count, char value[]) {      this.value = value;      this.offset = offset;      this.count = count;  }   public String substring(int beginIndex, int endIndex) {      //check boundary      return  new String(offset + beginIndex, endIndex - beginIndex, value);  }

实际上substring并没有去new 一个String对象,substring返回的字符串和之前的字符串是共用的一个字符数组。

只是数组的起点和长度改变了。所以之前的那个被截取的字符串就没有(也不能)被回收。

如果你想要让它能被回收,可以这样substring.

x.substring(a,b)+""  //or  new String(x.substring(a,b))

前者等同于:

StringBuilder sb = new StringBuilder();  sb.append(x.substring(x, y));  sb.append("");  x = sb.toString();

所以,使用new String(x.substring(a,b))的方式效率更高。

 

原文:http://www.programcreek.com/2013/10/the-introduction-of-memory-leak-what-why-and-how/