繁体   English   中英

2.1破解编码面试中的垃圾收集

[英]Garbage Collection in 2.1 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;
    }
}

当您调用prev.next = n.next; n仍可立即用作方法中的变量。 它无处不在,处在生命的黄金时期。 仅当绝对无法访问对象时,对象才会被垃圾回收。

如果要立即跟进,则n = null; ,那么可以肯定,对象n指向的对象可能会被垃圾回收(假设您没有将其存储在其他任何地方)。 不是马上就引起您的注意,但最终。 关键是,什么时候进行垃圾回收都没有关系,因为您不再有任何方法可以将对该对象的引用返回到您的代码中。

至于垃圾收集器,当你写

 del = del.next;

无论先前被删除引用(假设没有其他引用它)有资格被垃圾收集-如果变量del是引用数据的唯一的事情,一旦del被重新分配什么会引用了数据,这使得资格GC。 请注意,这并不意味着将立即进行GC处理。

顺便说一句,在您的代码中,您编写的位置

else 
    {
        prev.next = del.next;
    }

您将获得NullPointerException ,因为您正试图访问prev的成员,该成员已在上面设置为null。

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

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

我觉得,如果我们采用简单的案例并逐步了解正在发生的事情,则更容易理解(以一种简单的方式,实际的实现很可能使用计数器和更复杂的数据结构,但我偏离了方向)。 假设我们有一个链表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的局部变量,因此,那时它可能不符合进行垃圾收集的条件。

当然,这是对发生的事情的非常简化的浏览,但这是事物的本质。 如果您有兴趣,还应该研究终结器在实际回收内存时如何发挥作用。 软引用和弱引用也是非常有趣的主题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM