-4

这可能有点理论上,我需要这个,因为我正在设计自己的编程语言作为一个爱好项目的乐趣。我将内存管理视为“四代”。这是我自己的观点,与任何论文或学校无关。

第一代是平坦的内存块,应用程序只是以某种方式管理它。

第二代是 malloc/free 风格的堆。系统将处理内存,但您处理对象。

第三代是引用计数的分配/处置,它发生在“原地”,或者换句话说,在调用 new/delete 期间发生。

第四代就是你通常所说的垃圾收集器。这可能发生在另一个线程中,或者有时就像第三代一样可能发生在调用 new/delete 或其他过程期间。

主要区别在于第 4 代 GC 有时会“遍历”或“扫描”堆对象并检查其中一些对象是否“无根”,以便删除它们。这很复杂,有许多用于“扫描”的算法。有时这些算法名称中包含“颜色”一词。像“三色标记”或“三色计数异步并行”等。进行“堆扫描”的最明显原因是处理循环引用。因为没有(还没有?)其他方法来处理它。

好的,现在回答我的问题。

如果没有第 4 代 GC,什么可能是一个易于理解的“现实生活”的循环引用示例?当然你可以只输入两行代码,故意创建循环引用。但这不是我的问题。我想要一个“真实”的例子,我的意思是可以自然发生的事情,并且可以很容易理解。还有一些东西可以真正向任何新手展示,你可以有一个需要用 GC 处理的循环引用。也许我也可以用“实用”和“有用”来描述这样一个例子。

4

1 回答 1

2

既然你要求一个真实的例子,这里有一个 Java 中的例子,使用LinkedList

{
    List<String> list = new LinkedList<String>();
    list.add("hello");
    list.add("world");
}

为什么这里有一个循环?因为JavaLinkedList是一个双向链表,它的实现add是:

boolean add(E e) {
     linkLast(e);
     return true;
}

void linkLast(E e) {
     final Node<E> l = last;
     final Node<E> newNode = new Node<>(l, e, null);
     last = newNode;
     if (l == null)
         first = newNode;
     else
         l.next = newNode;
     size++;
     modCount++;
}

使用这个内部类和这些字段:

private static class Node<E> {
     E item;
     Node<E> next;
     Node<E> prev;

     Node(Node<E> prev, E element, Node<E> next) {
         this.item = element;
         this.next = next;
         this.prev = prev;
     }
 }

Node<E> first, last;

范围结束后,list没有引用,因此可以收集。但是从两个调用Node创建的两个 s每个都有两个引用——第一个来自and ,第二个来自and 。收集只会删除每个对象的其中一个引用,留下一个循环。linkLast()firstlast.prevlastfirst.nextlist

于 2013-06-10T06:47:30.337 回答