Вопрос о сборщике мусора Java, нулевых значениях и утечке памяти - PullRequest
4 голосов
/ 21 декабря 2008

Предположим, я реализую очередь в java, и у меня есть ссылка на начальный узел, называемый ini, а другой - на последний, называемый last. Теперь я начинаю вставлять объекты в очередь. В какой-то момент я решил, что хочу выполнить операцию по очистке очереди. Затем я делаю это:

ini = null;
last = null;

У меня течет память? Узлы между ini и last все еще связаны друг с другом и, я думаю, все еще имеют свои данные, но в то же время есть сборщик мусора.

Альтернативой было бы получить доступ к каждому элементу, а затем обнулить их ссылки на следующий узел, но тогда я бы в основном делал это, как в C ++, за исключением того, что я бы не использовал явно удаление.

Ответы [ 5 ]

19 голосов
/ 21 декабря 2008

Пока ни один элемент в очереди не упоминается где-либо еще в вашем коде, сборщик мусора сможет освободить эту память. Установка указателей на null в Java не такая же, как в C, где установка указателя malloc на null предотвращает его освобождение. В Java память восстанавливается, когда она больше недоступна. В Java нет утечек памяти (в смысле C / C ++), если вы не используете нативный код через JNI.

Упрощенный сборщик мусора будет просто подсчитывать количество ссылок на объект и освобождать этот объект, когда счетчик ссылок достигнет нуля, но он не сможет справиться с циклами ссылок (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;}
3 голосов
/ 27 февраля 2009

Да, GC работает в этом случае. Но элементы между головой и хвостом могут выжить и затем войти в пространство старого поколения, а затем они будут собраны во время полной GC. Как известно, полный сборщик мусора стоит дорого. Что касается производительности, то лучше обнулить их.

Вы можете видеть, как реализован метод clear () в java.util.LinkedList.

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 голосов
/ 21 декабря 2008

Это будет хорошо работать. GC обнаружит, что узлы недоступны, поэтому все они будут очищены.

0 голосов
/ 28 февраля 2009

Если вы подозреваете, что у вас утечка памяти, я предлагаю вам использовать профилировщик памяти, чтобы увидеть, как объекты сохраняются с течением времени. При таком инструменте будет очевидна быстрая утечка памяти, поэтому, если вы создадите тест для чего-то, о чем вы подозреваете утечки, и повторите его много раз, вы сможете увидеть утечку и узнать, почему объекты сохраняются.

0 голосов
/ 21 декабря 2008

Вот некоторый код, демонстрирующий, как блуждающий дескриптор в середине структуры списка может удержать 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;
}
...