关于Java垃圾收集器、null和内存泄漏的问题

关于Java垃圾收集器、null和内存泄漏的问题,java,garbage-collection,reference,Java,Garbage Collection,Reference,假设我正在用java实现一个队列,我有一个对初始节点的引用,称为ini,另一个对最后一个节点的引用,称为last。现在,我开始向队列中插入对象。有一次,我决定执行一个操作来清除队列。然后我会这样做: ini = null; last = null; 我在泄漏内存吗?ini和last之间的节点都是链接的,仍然有数据,我猜,但同时还有垃圾收集器 另一种方法是访问每个元素,然后将它们的引用归为下一个节点,但之后我基本上像C++那样做,除非我不明确地使用DELL.< P>,只要队列中没有任何项引用到代

假设我正在用java实现一个队列,我有一个对初始节点的引用,称为ini,另一个对最后一个节点的引用,称为last。现在,我开始向队列中插入对象。有一次,我决定执行一个操作来清除队列。然后我会这样做:

ini = null;
last = null;
我在泄漏内存吗?ini和last之间的节点都是链接的,仍然有数据,我猜,但同时还有垃圾收集器


另一种方法是访问每个元素,然后将它们的引用归为下一个节点,但之后我基本上像C++那样做,除非我不明确地使用DELL.

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

一个过于简单的垃圾收集器只会计算对一个对象的引用数,并在引用数为零时释放该对象,但这将无法处理引用周期(A->B、A->B->C->A等)。JavaGC算法进行活动性测试,在测试中,它们构建系统中所有对象的参考图。GC进行图形遍历,任何不可访问的节点(对象)都被标记为未使用且可重新分配。图的根(遍历的起始点)包括线程堆栈上的变量、静态变量和本机代码通过JNI持有的引用。请参阅此处的更多信息:

仍然可能存在参考泄漏。这指的是您对对象的引用的保留时间超过需要的时间的情况。例如:

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()之后,数组成员变量仍然具有对返回对象的引用。它将防止对象被垃圾收集,直到周围的堆栈实例不再被引用。这是极少数需要将变量设置为null以回收其内存的情况之一:

public Object pop() {Object ret = stack[top]; stack[top--] = null; return ret;}

那很好。GC将检测到节点不可访问,因此它们都将被清除。

以下是一些代码,用于演示列表结构中间的错误句柄如何阻止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;
}
import java.lang.ref.*;
公共类MemoryLeak1{
MyListItem leakedItem=null;
WeakReference[]refs=null;
公共静态void main(字符串[]args){
WeakReference ref=null;
MyListItem项=null;
MemoryLeak1泄漏=新的MemoryLeak1();
int i;
leak.doit();//创建内存泄漏
System.gc();//强制gc运行;
//此时,列表已被明确清除,
//已超出范围,并且GC已运行。
//但是,leak.leakedItem仍然持有
//引用列表中的某个项目,所以
//可从该项访问的对象仍处于活动状态。
//展示仍然存在的东西。。。
对于(i=0;i<10;i++){
ref=泄漏。refs[i];
item=(MyListItem)ref.get();
如果(item==null){System.out.println(“+i+”=null”);}
else{System.out.println(“+i+”=“+(String)item.thing);}
}
System.out.println(“--------------------------”;
//现在让我们释放一些额外的项目。。。

对于(i=1;iYes),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++;
}
public void clear(){
条目e=标题。下一步;
while(e!=标题){
条目next=e.next;
e、 next=e.previous=null;
e、 元素=空;
e=下一个;
}
header.next=header.previous=header;
尺寸=0;
modCount++;
}

触及问题。

如果您怀疑内存泄漏,我建议您使用内存探查器查看对象在一段时间内是如何被保留的。使用这种工具,快速内存泄漏将是显而易见的,因此如果您为怀疑泄漏的内容创建测试并重复多次,您将能够看到泄漏以及对象被保留的原因etained.

缺少节点的其他PTR是关键。如果任何节点都可以访问,它们都可以访问(假设列表中的下一个和上一个PTR)clayton,真的吗?这意味着它们无法访问,但它们仍然不会被垃圾收集。我不知道,但这听起来很奇怪。这是否更像是如果对对象的最后一个直接或间接堆栈引用丢失,那么对象就被释放了?litb:如果你对列表中的某个项有ptr,那么e列表项都与下一个/上一个PTR链接在一起,将列表的开头和结尾设置为null并不会使它们无法访问,因为您仍然有一个链接到列表的“中间”。litb:我认为这就是为什么在实现链接列表时,您没有将列表中的实际项链接在一起的原因。您使用了一个特殊的“ListItem”对象,并让它指向存储在那里的对象。只要不保留ListItem对象的ptr,就可以了。System.gc()不会强制垃圾收集器运行。Bombe:好的,“force”可能不是正确的词,但这里来自api文档:“调用gc方法表明[JVM]努力回收未使用的对象……当控制从方法调用返回时,[JVM]已尽最大努力回收sp