Почему у объекта, созданного ClassLoader, нет шансов собрать мусор сам по себе

Я имею в виду этот пример кода, о котором сообщается в 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 local;
    private static Weakling staticRef;
    private Object var = new byte[1000*1000];
    public Weakling() {
        local = new ThreadLocal();
        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, не имеет шансов собрать мусор сам?

13
задан Cheok Yan Cheng 22 August 2010 в 04:33
поделиться

1 ответ

Механизм 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 ] Вход.

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

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

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

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

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

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

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

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)

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

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 ()
  • Класс Weakling (50000296) строго ссылается на экземпляр ThreadLocal (500002a4) ) через статическую переменную класса

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

22
ответ дан 1 December 2019 в 22:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: