【问题标题】:Recursive object deletion递归对象删除
【发布时间】:2014-07-05 07:12:57
【问题描述】:

我很难创建一种方法来递归删除对具有以下类的对象的所有引用:

class CoolObject
{
    int someData;
    CoolObject[] subObjects; // array and each element get initialized elsewhere

    void nullSubObjects()
    {
        if(subObjects != null)
        {
            for (int o = 0; o < subObjects.length; o++) 
            {  
                if (subObject[o] != null)
                {
                  subObject[o].nullSubObjects(); //returns exception here
                  subObject[o] = null;
                } 
            } 
            subObjects = null; 
        }
    }
}

Class elsewhere
{

    void update(CoolObject currentObject)
    {
        //test for creation
        if(currentObject.subObject == null && and other conditions)
        {
            currentObject.subObject = new CoolObject[52];

            for(int m = 0; m < 52; m++) 
                 currentObject.subObject[m] = new CoolObject();         
        }

        //test for deletion
        if(currentObject.subObject != null && and other conditions)
             currentObject.nullSubObjects();



        //recursive update
        if(currentObject.subObjects != null)
             for (int q = 0; q < 52; q++) update(currentObject);
     }
}

每当我去更新结构时,它都会导致循环内出现空指针异常,尽管刚刚检查过以确保子对象不为空...

为什么会出现此异常?

如果不明确取消它,'someData' 会阻止它们的 GC 吗?


感谢大家的反馈!

这个实现是确保子对象更有可能被自动 GC 删除的一种过于复杂的方法。我已经决定在更高级别取消 subObjects 数组,并调用 System.gc()

如果不这样做,我原本 40k 的程序将在删除任何内容之前超过 2Gb。

【问题讨论】:

  • 修复它的方法是读取 NullPointerException 的堆栈跟踪以了解它被抛出的位置,从而了解什么是 null。
  • 除此之外,这是一种非常糟糕的代码格式化方式...非常不清楚。
  • 您的问题实际上是关于是否需要将所有内容设置为 null(您几乎可以肯定不需要)或为什么会出现异常?
  • 数组和每个元素都在别处初始化你能提供那个代码吗??
  • 您清楚地向我们展示了编写的代码,而不是向我们展示了实际代码。而且您还没有发布异常的堆栈跟踪。投票结束。

标签: java recursion null


【解决方案1】:

您无需执行任何这些操作即可使对象可被垃圾回收!

当一个对象不再可访问时,即当它没有可访问的引用时,它就会变成垃圾。

举个例子:

class A {
    int i;
}

class B {
    A a = new A();
}

class Main {
    public static void main(String[] args) {
        B b = new B();
        // point 1
        b = null;
        // point 2
    }
}

point 1,存在B 类型的对象,由b 引用,并且存在A 类型的对象,由b.a 引用。

你可以想象一个这样的引用链:

+ main
| + object of B
  | - object of A

point 2,我无法再访问B 类型的对象,因此它可以进行垃圾回收。我也无法访问A 类型的对象,所以它也会被垃圾回收。

现在引用链看起来像这样:

+ main 
| (no references)

+ object of B
| - object of A

即使B 类型的对象引用了A 类型的对象,仍然无法从main 获取该对象。

当没有程序实际使用的对象引用链时,该对象将被垃圾回收。

【讨论】:

  • 在我只调用subObjects = null的情况下;有一种内存泄漏......而这个类是唯一可能发生的地方。
  • @user3537499:不,没有内存泄漏。
  • @JBNizet:如果不频繁调用 System.gc(),在删除任何对象之前,该程序将远远超出预期的 RAM 使用量约 40k 到 2Gb... 是否存在你会用另一个术语来描述这个吗?
  • @user3537499 JBNize 正确。内存泄漏是指内存不可恢复(直到程序退出并且内存被操作系统回收)。
  • @user3537499:这不是内存泄漏。这就是 GC 决定在真正需要避免 OutOfMemoryErrors 之前不进行垃圾收集。如果您在启动 JVM 时使用要求更小的堆大小的选项,则 GC 将更频繁地收集。
【解决方案2】:

为什么会出现这个异常?

我能想到你可能会在那时获得 NPE 的几个原因。

  • 如果对象网络是循环的,则可以通过递归 nullSubObjects() 调用来访问 CoolObject 实例,同时它已经被处理。由于递归调用使subObjects 字段为空,因此当您从for 循环中间的递归返回并执行subObject[o].nullSubObjects(); 时,subObject 将是null,您将获得一个NPE。

  • 如果在您执行此操作时某个其他线程正在更新图表,则所有赌注都将关闭。此代码没有同步。

我还要注意这不是真正的代码(它不可编译)。真正的问题可能是只存在于真实代码上的东西。


是否有更安全的方法来确保删除所有引用?

这应该避免第一个问题:

    if (subObjects != null)
    {
        CoolObject[] s = subObjects;
        subObjects = null;
        for (int o = 0; o < s.length; o++) 
        {  
            if (s[o] != null)
            {
              s[o].nullSubObjects(); //returns exception here
              s[o] = null;
            } 
        }
    }

但是,这可能是不必要的;见下文。

如果不显式取消它,'someData' 会阻止它们的 GC 吗?

一旦对它的所有现有引用变得无法访问,整个树/图将被垃圾收集。很难想象这样的情况有必要清空所有引用。

【讨论】:

    猜你喜欢
    • 2016-06-19
    • 1970-01-01
    • 2021-05-29
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多