Почему объект, созданный ClassLoader, не имеет шанса собрать мусор сам - PullRequest
13 голосов
/ 22 августа 2010

Я имею в виду этот пример кода, о котором сообщается в http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6254531

import java.net.URL;

class Loader {
    public static void main(String[] args) throws Exception {
        for (;;) {
            System.gc();
            System.out.print(".");
            System.out.flush();
            new java.net.URLClassLoader(
                new URL[] { new java.io.File(".").toURL() },
                ClassLoader.getSystemClassLoader().getParent()
            ).loadClass("Weakling").newInstance();
        }
    }
}
public class Weakling {
    private static ThreadLocal<Object> local;
    private static Weakling staticRef;
    private Object var = new byte[1000*1000];
    public Weakling() {
        local = new ThreadLocal<Object>();
        local.set(this);
        staticRef = this;
    }

    @Override
    protected void finalize() {
        System.out.print("F");
        System.out.flush();
    }
}

Финализация никогда не будет вызвана. Однако, если я изменю

            new java.net.URLClassLoader(
                new URL[] { new java.io.File(".").toURL() },
                ClassLoader.getSystemClassLoader().getParent()
            ).loadClass("Weakling").newInstance();

до

new Weakling();

Работает очень хорошо, утечки не обнаружено.

Может кто-нибудь объяснить, почему объект, созданный ClassLoader, не имеет шансов собрать мусор сам?

1 Ответ

22 голосов
/ 22 августа 2010

Механизм ThreadLocal эффективно сохраняет в текущем потоке WeakHashMap экземпляров ThreadLocal для значений.Следовательно, если экземпляр ThreadLocal никогда не становится слабо ссылочным, тогда запись фактически просочилась.

Необходимо рассмотреть два случая.Для простоты обсуждения предположим, что ThreadLocal на самом деле хранит WeakHashMap в Thread.currentThread ();в действительности он использует более сложный механизм, который имеет эквивалентный эффект.

Сначала рассмотрим сценарий "нового ослабления":

  • На первой итерации цикла:
    1. класс Weakling загружается из загрузчика системного класса
    2. конструктор Weakling называется
    3. статическая переменная Weakling.local устанавливается с нуля на новый экземпляр ThreadLocal # 1
    4. ThreadLocal WeakHashMap обновляется для хранения нового экземпляра Weakling # 1
  • На всех последующих итерациях цикла:
    1. класс Weakling уже загружен изсистемный загрузчик классов
    2. Конструктор Weakling называется
    3. , статическая переменная Weakling.local устанавливается из старого экземпляра ThreadLocal # 1 в новый экземпляр ThreadLocal # 2.На старый экземпляр ThreadLocal # 1 теперь только (слабо) ссылается WeakHashMap.
    4. ThreadLocal WeakHashMap обновляется для хранения нового экземпляра Weakling.Во время этой операции WeakHashMap замечает, что старый экземпляр ThreadLocal # 1 является слабо ссылочным, поэтому он удаляет запись [ThreadLocal instance # 1, Weakling # 1] из карты перед добавлением [ThreadLocal instance # 2, Weakling # 2] entry.

Во-вторых, рассмотрим сценарий "новый URLClassLoader (...). loadClass (...). newInstance ()":

  • На первой итерации цикла:
    1. Класс Weakling # 1 загружается из URLClassLoader # 1
    2. Конструктор Weakling называется
    3. Weakling.local # 1статическая переменная устанавливается с нуля на новый экземпляр ThreadLocal # 1
    4. ThreadLocal WeakHashMap обновляется для хранения нового экземпляра Weakling # 1
  • на всех последующих итерацияхloop
    1. Слабый класс #n загружается из URLClassLoader # n
    2. Слабый конструктор называется
    3. Статическая переменная Weakling.local #n устанавливается с нуля на новыйThreadLocalЭкземпляр # n
    4. ThreadLocal WeakHashMap обновляется для хранения нового экземпляра Weakling.

Обратите внимание, что на этом последнем шаге экземпляр ThreadLocal # 1 имеет значение не слабо ссылочный.Это происходит из-за следующей цепочки ссылок:

  • Значение WeakHashMap строго ссылается на экземпляр Weakling # 1
  • Weakling instance # 1 строго ссылается на класс # 1 Weakling через Object.getClass ()
  • Слабый класс # 1 строго ссылается на экземпляр ThreadLocal # 1 через переменную статического класса

Пока цикл продолжает работать, в ThreadLocal WeakHashMap добавляется больше записей и сильная ссылкаЦепочка от значения к ключу (экземпляр Weakling до ThreadLocal) в WeakHashMap предотвращает сбор мусора устаревших записей.

Я изменил программу Loader, чтобы выполнить итерацию 3 раза, а затем дождаться ввода данных пользователем.Затем я сгенерировал дамп кучи с помощью java -Xrunhprof: heap = dump и ctrl-pause / break.Ниже приводится мой анализ окончательного дампа кучи:

Во-первых, есть три объекта Weakling:

OBJ 500002a1 (sz=16, trace=300345, class=Weakling@50000296)
OBJ 500003a4 (sz=16, trace=300348, class=Weakling@5000039d)
OBJ 500003e0 (sz=16, trace=300342, class=Weakling@500003d9)

Обратите внимание, что созданы все три экземпляра Weakling (500002a1, 500003a4 и 500003e0)из трех отдельных экземпляров классов (50000296, 5000039d и 500003d9 соответственно).Глядя на первый объект, мы видим, что он содержится в качестве значения в объекте записи в карте threadLocal:

OBJ 500002a5 (sz=32, trace=300012, class=java.lang.ThreadLocal$ThreadLocalMap$Entry@5000014b)
        referent        500002a4
        queue           500009f6
        value           500002a1

Референт здесь - это значение, удерживаемое слабо:

OBJ 500002a4 (sz=16, trace=300347, class=java.lang.ThreadLocal@50000125)

Поиск, мы видим, что этот объект хранится как значение в статическомпеременная "local" вышеупомянутого класса Weakling:

CLS 50000296 (name=Weakling, trace=300280)
        super           50000099
        loader          5000017e
        domain          50000289
        static local    500002a4
        static staticRef        500002a1

В заключение, мы имеем следующий сильный цикл цепочки ссылок для этого экземпляра Weakling, который предотвращает сбор мусора.

  • Значение WeakHashMap (500002a5) строго ссылается на экземпляр Weakling (500002a1)
  • Экземпляр Weakling (500002a1) строго ссылается на класс Weakling (50000296) через Object.getClass ()
  • Слабый класс (50000296) строго ссылается на экземпляр ThreadLocal (500002a4) через статическую переменную класса

Аналогичный анализ на других объектах Weakling показал бы аналогичный результат. Разрешение программе запускаться для дополнительных итераций показывает, что объекты продолжают накапливаться таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...