4

假设我在 java 中实现了一个队列,并且我有一个对初始节点的引用,称为 ini,另一个对最后一个节点,称为 last。现在,我开始将对象插入队列。在某一时刻,我决定我想要一个清除队列的操作。然后我这样做:

ini = null;
last = null;

我在泄漏内存吗?我猜 ini 和 last 之间的节点仍然是链式的并且仍然有它们的数据,但同时还有垃圾收集器。

另一种方法是访问每个元素,然后将它们对下一个节点的引用设为空,但是我基本上会像在 C++ 中那样做,除了我不会显式使用删除。

4

5 回答 5

19

只要在代码中的其他任何地方都没有引用队列中的项目,垃圾收集器就能够回收该内存。在 Java 中将指针设置为 null 与在 C 中将 malloc 的指针设置为 null 会阻止它被释放是不同的。在 Java 中,内存在不再可访问时被回收。只要您不通过 JNI 使用本机代码,Java 就没有内存泄漏(在 C/C++ 意义上)。

一个简单的垃圾收集器只会计算对一个对象的引用数并在引用计数达到零时释放该对象,但这将无法处理引用周期(A -> B,A -> B -> C -> A 等)。Java GC 算法会进行活性测试,在其中构建系统中所有对象的参考图。GC 会进行图遍历,并且任何无法访问的节点(对象)都被标记为未使用且可用于重新分配。图的根(遍历的起始点)包括线程堆栈上的变量、静态变量以及本机代码通过 JNI 持有的引用。在此处查看更多信息:http: //java.sun.com/developer/Books/performance/performance2/appendixa.pdf

仍然可能有参考泄漏。这是指您持有对对象的引用超过所需时间的情况。例如:

public class Stack {
  private final Object[] stack = new Object[10];
  private int top = 0;
  public void push(Object obj) {stack[top++] = obj;}
  public Object pop() {return stack[top--]; }
}

忽略上溢/下溢的可能性,在调用 Stack.pop() 之后,数组成员变量仍然具有对返回对象的引用。它将防止该对象被垃圾收集,直到不再引用周围的 Stack 实例。这是需要将变量设置为 null 以便可以回收其内存的罕见情况之一:

public Object pop() {Object ret = stack[top]; stack[top--] = null; return ret;}
于 2008-12-20T22:43:15.450 回答
3

那会很好用。GC 会检测到节点不可达,所以它们都会被清理掉。

于 2008-12-20T22:44:00.707 回答
3

是的,GC 在这种情况下有效。但是head和tail之间的元素可能会存活下来,然后进入老年代空间,然后在full GC期间被收集。如您所知,完整的 GC 很昂贵。就性能而言,将它们归零更好。

您可以看到 java.util.LinkedList 的 clear() 方法是如何实现的。

public void clear() {
    Entry<E> e = header.next;
    while (e != header) {
        Entry<E> next = e.next;
        e.next = e.previous = null;
        e.element = null;
        e = next;
    }
    header.next = header.previous = header;
    size = 0;
    modCount++;
}

http://tech.puredanger.com/2009/02/11/linkedblockingqueue-garbagecollection/触及问题。

于 2009-02-27T10:32:03.863 回答
0

如果您怀疑自己有内存泄漏,我建议您使用内存分析器来查看对象是如何随时间保留的。使用这样的工具,快速内存泄漏将是显而易见的,因此,如果您为怀疑泄漏的内容创建测试并重复多次,您将能够看到泄漏以及保留对象的原因。

于 2009-02-27T21:41:48.950 回答
-1

下面是一些代码,用于演示列表结构中间的杂散句柄如何阻止 GC 完全清理:

import java.lang.ref.*;

public class MemoryLeak1 {

    MyListItem leakedItem = null;
    WeakReference[] refs = null;

    public static void main(String[] args) {
        WeakReference ref = null;
        MyListItem item = null;
        MemoryLeak1 leak = new MemoryLeak1();
        int i;

        leak.doit(); // create a memory leak
        System.gc(); // force the gc to run;

        // At this point the list has been explicitly cleared,
        // has gone out of scope, and the GC has run.
        // However, leak.leakedItem is still holding a
        // reference to an item in the list, so anything
        // reachable from that item is still alive.

        // show what's still around...
        for (i = 0; i < 10; i++) {
            ref = leak.refs[i];
            item = (MyListItem)ref.get();
            if (item == null) { System.out.println("" + i + " = null"); }
            else { System.out.println("" + i + " = " + (String)item.thing); }
        }
        System.out.println("---------------------");

        // now let's free some additional items...
        for (i = 1; i <= 3; i++) {
            item = leak.leakedItem;
            leak.leakedItem = item.next;
            leak.leakedItem.prev = null;
            item.prev = null;
            item.next = null;
        }
        item = null;

        System.gc(); // force the gc to run again

        // this time we should get fewer items
        for (i = 0; i < 10; i++) {
            ref = leak.refs[i];
            item = (MyListItem)ref.get();
            if (item == null) { System.out.println("" + i + " = null"); }
            else { System.out.println("" + i + " = " + (String)item.thing); }
        }
        System.out.println("---------------------");

        // now clear the last reference
        leak.leakedItem = null;

        System.gc(); // force the gc to run again

        // this time we should none
        for (i = 0; i < 10; i++) {
            ref = leak.refs[i];
            item = (MyListItem)ref.get();
            if (item == null) { System.out.println("" + i + " = null"); }
            else { System.out.println("" + i + " = " + (String)item.thing); }
        }
        System.out.println("---------------------");
    }

    public void doit() {
        this.refs = new WeakReference[10];
        MyList list = new MyList();
        MyListItem item = null;

        // add strings to the list.
        // set each into the array of soft refs 
        // set a ptr to the 6th item in an instance variable
        for (int i = 0; i < 10; i++) {
            item = new MyListItem();
            item.thing = new String("string" + i);
            list.insert(item);
            if (i == 5) this.leakedItem = item;
            this.refs[i] = new WeakReference(item);
        }

        // clear the list, but don't clear the
        // additional ptr to the 6th item
        list.clear();
    }
}

class MyList {
    MyListItem head = null;
    MyListItem tail = null;

    void clear() {
        head = null;
        tail = null;
    }

    void insert(MyListItem item) {
        if (head == null) {
            // empty list
            item.next = null;
            item.prev = null;
            tail = item;
            head = item;
        }
        else if (head == tail) {
            // one item in list
            item.next = head;
            item.prev = null;
            tail = head;
            head = item;
        }
        else {
            // multiple items in list
            item.next = head;
            item.prev = null;
            head = item;
        }
    }

    MyListItem remove() {
        MyListItem item = tail;
        if (item != null) {
            tail = item.prev;
            if (tail == null) {
                head = null;
            }
            else {
                tail.next = null;
            }
            item.next = null;
            item.prev = null;
        }
        return item;
    }
}

class MyListItem {
    MyListItem next = null;
    MyListItem prev = null;
    Object thing = null;
}
于 2008-12-21T02:07:50.967 回答