【问题标题】:Garbage Collection in 2.1 Cracking the Coding Interview2.1 破解编码面试中的垃圾收集
【发布时间】:2014-01-04 00:36:36
【问题描述】:

对于这段代码(Cracking the Coding Interview 2.1 的解决方案):当你做prev.next = n.next 时,n 会被垃圾收集器收集吗?那你怎么办n = n.next

2.1的问题是:编写代码从未排序的链表中删除重复项。

有人可以向我解释一下垃圾收集器在这种情况下是如何工作的吗?

public static void removeDup (LinkedListNode del)
{
    LinkedListNode prev = null;
Hashtable myTable = new Hashtable();

    while(del!= null)
    {


        // table does not have the key yet
        if (myTable.containsKey(del.data) == false)
        {
            myTable.put(del.data, true);
            prev = del;
        }

        // table has the duplicate
        else 
        {
            prev.next = del.next;
        }
        del = del.next;
    }
}

【问题讨论】:

  • 对不起,Hashtable table = new Hashtable();应该在while循环之外。我的错。我已经在代码中解决了这个问题。
  • 文体说明,你为什么使用 Hashtable?您实际上并不关心分配给您提取的每个键 (del.data) 的值 (true)。 Set / HashSet 可能会让你的代码看起来更干净 :)

标签: java garbage-collection linked-list


【解决方案1】:

当您调用prev.next = n.next; 时,n 仍然立即可用作为您方法中的变量。它不会去任何地方,它正处于生命的黄金时期。 只有在您绝对无法访问对象时才会对对象进行垃圾回收。

如果您要立即使用n = null; 跟进,那么可以肯定,n 指向的对象可能会被垃圾回收(假设您没有将它存储在其他任何地方)。不是马上介意你,而是最终。关键是,垃圾回收发生的时间并不重要,因为您不再有任何方法可以将对该对象的引用返回到您的代码中。

【讨论】:

    【解决方案2】:

    您在方法中将del 重新分配为del = del.next;。因此,如果没有其他直接或间接指向 del 的引用,旧的 del 可能有资格进行垃圾回收。

    当对象超出范围并且没有其他引用指向该对象时,对象将被垃圾回收。

    【讨论】:

      【解决方案3】:

      关于垃圾收集器,当你写的时候

       del = del.next;
      

      del 之前引用的任何内容(假设没有其他内容引用它)有资格被垃圾收集 - 如果变量 del 是唯一引用该数据的东西,则一次 del被重新分配,不再引用该数据,这使得它有资格进行 GC。请注意,这并不意味着它会立即被 GC。

      顺便说一句,在你的代码中,你写的地方

      else 
          {
              prev.next = del.next;
          }
      

      你会得到一个NullPointerException,因为你正在尝试访问prev的成员,该成员已在上面设置为null。

      【讨论】:

      • 没有 NPE,prev.next = del.next,永远不会被执行。 Hashtable 会在每个循环中重新创建,因此永远不会包含密钥!如果 Hashtable 应该在循环之外,那么 prev 也将设置为 del,我们知道 del 在该行执行之前不为空。
      【解决方案4】:

      我觉得,如果我们举一个简单的案例,并了解正在发生的事情,会更容易理解(以简单的方式,实际实现可能使用计数器和更复杂的数据结构,但我离题了)。假设我们有一个 1 -> 1 -> 2 -> null 的链表传递给这个方法,然后在我们第一次进入 while 循环之前查看堆栈和堆上的内容:

      Heap: { 
        #1 : { LLN, data: 1 , next #2    }, 
        #2 : { LLN, data: 1 , next #3    }, 
        #3 : { LLN, data: 2 , next: null },
        #4 : { HashTable, keyvalues: {} } 
      }
      Stack : {
        del: #1
       prev: null
       myTable: #4
      }
      

      从这张图片中省略了调用函数的堆栈上的局部变量,当然,一直回到链的顶部,以及我们不知道的任何其他分配的对象。

      我们还知道一个对象(分配在堆上)当它变得无法被任何代码访问时就有资格进行垃圾回收。或者换句话说:如果我不可能从引用开始在堆栈上,并按照堆上的引用到达特定位置,它有资格被垃圾收集器清除。

      让我们通过 while 循环进行一次迭代(你应该以这个结束):

      Heap: { 
        #1 : { LLN, data: 1 , next: #2    }, 
        #2 : { LLN, data: 1 , next: #3    }, 
        #3 : { LLN, data: 2 , next: null  },
        #4 : { HashTable, keyvalues: {1:true} } 
      }
      Stack : {
        del: #2
       prev: #1
       myTable: #4
      }
      

      我们仍然看到一切都是可以到达的,就像以前一样。让我们进入第二次迭代,我们结束了 else 块,并执行 do:prev.next = del.next。由于 del.next = #3,我们为 prev.next 分配了该值并以以下方式结束:

      Heap: { 
        #1 : { LLN, data: 1 , next: #3    }, 
        #2 : { LLN, data: 1 , next: #3    }, 
        #3 : { LLN, data: 2 , next: null  },
        #4 : { HashTable, keyvalues: {1:true} } 
      }
      Stack : {
        del: #2
       prev: #1
       myTable: #4
      }
      

      此时您可以看到堆上的所有内容都还活着(不符合垃圾回收条件)。 #1 被 prev 引用,#2 被 del 引用,#4 被 myTable 引用,#3 被 #1 和 #2 引用,它们都是活动的。现在看看我做del = del.next时会发生什么:

      Heap: { 
        #1 : { LLN, data: 1 , next: #3    }, 
        #2 : { LLN, data: 1 , next: #3    }, 
        #3 : { LLN, data: 2 , next: null  },
        #4 : { HashTable, keyvalues: {1:true} } 
      }
      Stack : {
        del: #3
       prev: #1
       myTable: #4
      }
      

      我的堆栈点上的任何东西(已知)现在都指向编号 #2,因此此时它可能有资格进行垃圾收集。正如我之前提到的,我们不知道堆栈的其余部分,因此其他东西可能会引用它,但很可能它已经死了并且可以回收。正如其他人已经提到的那样,此时不必进行垃圾收集,但如果确实如此,并且没有其他任何东西持有对#2的引用,它可以回收#2。

      如果你继续这个练习,你会注意到在 while 循环的下一次迭代中,#1 也将不再被我们从​​堆栈中知道的任何东西引用。但是,调用此方法的方法很可能有一个引用 #1 的局部变量,因此它可能不适合进行垃圾收集。

      这当然是对正在发生的事情的一个非常简单的介绍,但它是事情的概要。如果您有兴趣,还应该研究终结器在实际回收内存时如何发挥作用。软引用和弱引用也是非常有趣的话题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        • 2011-01-21
        相关资源
        最近更新 更多