【问题标题】:Recursively nullify member objects in Java?递归地使Java中的成员对象无效?
【发布时间】:2013-09-13 18:36:10
【问题描述】:

作为另一个对象成员的对象是否有资格在父对象进行垃圾收集时?例如,让我们想象一下这个场景:

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() 来达到这个目的吗?

【问题讨论】:

  • 永远不要依赖finalize() 做任何事情。在这种情况下,object_2 和 object_3 引用的对象也会被收集,因为它们无法被代码访问。

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


【解决方案1】:

这是孤立岛的典型例子。

是的..所有标记为空的对象以及对象岛(对象相互引用);但它们都无法到达)收集垃圾。

"Island of isolation" of Garbage Collection 上看到这篇不错的帖子

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

Finalize 不是一种进行垃圾收集的方法。基本上,当该类的对象被垃圾收集时,finalize 让您有机会做某事。但是,甚至不要依赖 finalize 方法进行任何清理,因为一个对象可能永远不会被垃圾回收,因此永远不会调用 finalize。

查看更多关于 finalize When is the finalize() method called in Java?

【讨论】:

    【解决方案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 的东西。

    【讨论】:

      猜你喜欢
      • 2011-02-20
      • 2019-10-26
      • 1970-01-01
      • 2010-12-31
      • 2018-06-23
      • 1970-01-01
      • 2022-11-17
      • 2018-07-03
      • 1970-01-01
      相关资源
      最近更新 更多