关于Java垃圾收集器,空值和内存泄漏的问题

时间:2008-12-20 22:38:43

标签: java garbage-collection reference

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

ini = null;
last = null;

我是否在泄漏记忆?我想,ini和last之间的节点都是链接的,但仍有数据,但同时还有垃圾收集器。

另一种方法是访问每个元素,然后将它们对下一个节点的引用置空,但是我基本上就像在C ++中一样,除了我不会明确地使用delete。

5 个答案:

答案 0 :(得分: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;}

答案 1 :(得分:3)

这样可以正常工作。 GC将检测到节点无法访问,因此它们都将被清除。

答案 2 :(得分:3)

是的,GC适用于这种情况。 但是头部和尾部之间的元素可以存活,然后进入老一代空间 它们将在完整的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/触及了这个问题。

答案 3 :(得分:0)

如果您怀疑内存泄漏,我建议您使用内存分析器查看对象随时间的保留情况。使用这样的工具会出现快速内存泄漏,因此如果您为怀疑泄漏的内容创建测试并重复多次,您将能够看到泄漏以及保留对象的原因。

答案 4 :(得分:-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;
}