秋梦无痕

一场秋雨无梦痕,春夜清风冻煞人。冬来冷水寒似铁,夏至京北蟑满城。

Avatar

Inner Class和内存泄漏

发信人: kiwi (冬虫夏草), 信区: Java
标 题: Inner Class和内存泄漏
发信站: BBS 水木清华站 (Thu Jan 15 12:49:17 2004), 转信

这是我工作中解决内存泄漏的时候碰到的问题,昨天在这里问到过有关的问题,现在将自己的认识共享。

class C {
public static Vector container = new Vector();

private class D {
}

public void addD() {
container.add(new D());
}

public static void main(String[] args) {
while(true) {
C aC = new C();
aC.addD();
aC = null;
try {
Thread.sleep(10000); //sleep 10 seconds to do GC with JProfiler
//Do GC, how many instances of C and D will leave in memory?
}catch(InterruptedException e) {
}
}
}
}

在上面的做GC的地方,每个循环后,无疑有一个D对象会增加,因为它被加入到静态的container中。那么C呢?C在做操作以后被置成null了,应该能够被GC释放吧?不是的!每次循环后内存中会增加一个C对象,它无法被释放。
注意D是C的Non-static Inner Class,一个非静态的Inner Class只能由这个类的某个非静态的方法创建,这个Inner Class是能够读写它的Outer Class的非私有数据的,这是因为Java编译器在编译Inner Class时在类的定义里“偷偷”增加了一个域this$0,它指向生成Inner Class的对象实例,所以在D里面有一个C的引用!这样D被静态的container所引用,C又被D引用,这样aC即使被置为null,如果D不被释放,aC也不会被释放,这就造成了Memory Leak。这种内存泄漏十分的隐蔽!

解决这类Memory Leak的方法就是将Inner Class定义成静态(static)的,这样即使D对象不被释放,C对象也会被释放掉,这样就防止了这类内存泄漏。在<<Think in Java 3rd>>的第8章中讲到了Inner Class,其中有一句话这样说:“If you don’t need a connection between the inner class object and the outer class object, then you can make the inner class static. ”当然,如果Inner Class需要读取Outer Class对象的数据时,就不能定义成static,那么就要注意这种潜在的问题,及时释放Inner Class对象。

以上试验使用的JDK版本为1.3.1_06,观察内存情况使用了工具JProfiler 2.2.1。

发信人: windring (Hate), 信区: Java
标 题: Re: Inner Class和内存泄漏
发信站: BBS 水木清华站 (Thu Jan 15 13:23:58 2004), 转信

java中的变量出了作用域自然会被gc回收。是否显示的设成null是纯粹的多此一举,规范并没有保证设成null的变量一定立即回收,或者10秒以后回收,或者比不设null的变量先回收,这只取决于gc的调度方法,不应该由程序员来控制,程序员也不用关心,程序里面根本不应该出现这样的,否则java也就不称之为java了。

至于内存泄漏,我所见的有两种
1. 长时间运行的程序不断的向container中插入对象而没有释放或者及时释放
2. 一些不同于内存的资源(connection,socket,context等)没有调用close来释放

1取决于程序算法的设计,要考虑周全,并时时监控模块中关键的container数据结构的状态
2必须在编码时确定把close放在finally块中

你所说的这个归于1

btw: JProfiler可以检测并自动修正这两种情况吗?这个对于维护一些错漏百出的已有程序简直太游泳了

评论已关闭