在Java中递归地取消成员对象?

时间:2013-09-13 18:36:10

标签: java null memory-leaks member recursive-datastructures

当父对象出现时,属于另一个对象成员的对象是否有资格进行垃圾收集?例如,让我们想象一下这种情况:

MyClass_1的代码:

public class MyClass_1 {

    // Member object
    private MyClass_2 myClass_2;

    // Getter
    public MyClass_2 getMyClass_2() {
        return this.myClass_2;
    }

    // Setter
    public void setMyClass_2(MyClass_2 myClass_2) {
        this.myClass_2 = myClass_2;
    }
}

MyClass_2的代码:

public class MyClass_2 {

    // Member object
    private MyClass_3 myClass_3;

    // Getter
    public MyClass_3 getMyClass_3() {
        return this.myClass_3;
    }

    // Setter
    public void setMyClass_3(MyClass_3 myClass_3) {
        this.myClass_3 = myClass_3;
    }
}

好的,现在我们做了(MyClass_3的代码不相关):

// Instantiation of one root object
MyClass_1 object_1 = new MyClass_1();

// Composition of two more objects
object_1.setMyClass_2(new MyClass_2());
object_1.getMyClass_2().setMyClass_3(new MyClass_3());

// And now...
object_1 = null;

当然,此时object _ 1是垃圾收集,但是object_2object_3呢?我是否应该这样做以避免内存泄漏?

object_1.getMyClass_2().setMyClass_3(null);
object_1.setMyClass_2(null);
object_1 = null;

或者JVM是否自动执行该引用版本?如果需要手动完成,我可以依靠finalize()来实现这个目的吗?

2 个答案:

答案 0 :(得分:5)

这是隔离岛的典型例子。

是..所有标记为null的对象以及对象岛(对象互相引用);但是没有一个可以到达)垃圾收集。

"Island of isolation" of Garbage Collection

上查看这篇不错的帖子

在这种情况下,您不需要将My_class2和My_class3显式设置为null。一旦父项为空,GC也将回收它们。

Finalize不是垃圾收集的一种方式。基本上,finalize让你有机会做什么,当该类的对象被垃圾收集时。但即使不依赖于finalize方法进行任何清理,因为对象永远不会被垃圾收集,因此永远不会调用finalize。

详细了解最终确定When is the finalize() method called in Java?

答案 1 :(得分:2)

要了解对象何时被垃圾收集,您需要了解对象的分配方式。

所有程序都有两部分内存; 堆栈。随着程序的执行,它将输入新的功能或方法 - 这将创建一个新的范围。每当创建一个新的范围时,相应的新“框架”将被放置在堆栈的“顶部”。该框架引用了该范围内使用的所有变量。

创建对象时,例如通过new关键字(MyObject obj = new MyObject();),会在堆上留出一部分内存供该对象占用。然后,当前堆栈帧将引用对象所在堆中的位置。

显然,该对象可能引用堆上的其他对象。程序知道如何访问这些对象的方法是从堆栈上的引用移动到堆上的第一个对象,然后移动到直接或间接连接的任何其他对象。所有这些对象都在堆上。

当卸载堆栈帧时(当程序离开该范围时发生),该帧所持有的任何引用都将被释放。这可能意味着堆上的一个或多个对象不再由堆栈上的任何内容直接或间接引用。这些是可用于垃圾收集的对象。如果你想知道一个对象是否会被垃圾收集,你只需要询问它当前是否已连接到堆栈。

请注意,此类对象可用于垃圾回收,而不是自动垃圾回收。如此标记的任何物体将在收藏家选择时被拆除。可能永远不会。 finalize()对正在收集的任何对象运行:这可以确保收集时发生某些事情,但鉴于您无法保证它有用,但实用性有限。

现在,举个例子:

public void A() {//entering this method creates a new stack frame
  MyObject obj = new MyObject();//'obj' is a reference belonging to the current stack frame.
    //the obj reference is now pointing to a place in memory on the heap

  this.B();
}

public void B() { //Now we are in a new stack frame on top of the older one.
  obj.doSomething();//Doesn't work! There is no 'obj' reference in this stack frame
    //This results in a null pointer exception
  MyObject obj2 = new MyObject();//A totally new object is created on the heap!
} //When we leave this method we leave the scope, and the stack frame. 
 //obj2 is no longer referenced, and so is available for garbage collection

最后一点说明;让我们说你实例化三个对象; ABCC传递给AB并保存为这些对象的成员变量。现在假设您将范围保留在B的定义范围内。 C不是垃圾回收,因为A仍然有对它的引用;堆栈上还有一些地方正在抓住持有C的东西。