Подтвердить что ты не робот

Коллекция мусора ThreadLocal

Из javadoc

Каждый поток содержит неявную ссылку на свою копию локальной локальной переменной, если поток жив, и экземпляр ThreadLocal доступен; после того, как поток исчезнет, ​​все его копии локально-потоковых экземпляров подлежат сборке мусора (если нет других ссылок на эти копии).

из этого, кажется, что объекты, на которые ссылается переменная ThreadLocal, собираются мусором только тогда, когда поток умирает. Но что, если переменная ThreadLocal a больше не ссылается и подлежит сбору мусора? Будет ли объект ссылаться только на переменную a на предмет сбора мусора, если поток, содержащий a, все еще жив?

например, существует следующий класс с переменной ThreadLocal:

public class Test {
    private static final ThreadLocal a = ...; // references object b
}

Этот класс ссылается на некоторый объект, и этот объект не имеет других ссылок на него. Затем во время контекста undeploy приложение classloader становится объектом сбора мусора, но поток из пула потоков, поэтому он не умирает. Будет ли объект b подвергаться сбору мусора?

4b9b3361

Ответ 1

Переменные ThreadLocal сохраняются в Thread

ThreadLocal.ThreadLocalMap threadLocals;

который инициализируется лениво на первом ThreadLocal.set/get invocation в текущем потоке и содержит ссылку на карту до тех пор, пока Thread не будет жив. Однако ThreadLocalMap использует WeakReferences для ключей, поэтому его записи могут быть удалены, когда ThreadLocal ссылается из ниоткуда. Подробнее см. ThreadLocal.ThreadLocalMap javadoc.

Ответ 2

Если сам ThreadLocal собран, потому что он больше не доступен (там есть "и" в цитате), тогда весь его контент может быть в конечном итоге собран в зависимости от того, ссылается ли он на другое место и на другие манипуляции ThreadLocal происходят в одном потоке, вызывая удаление устаревших записей (см., например, replaceStaleEntry или expungeStaleEntry в ThreadLocalMap). ThreadLocal не является (сильно) ссылкой на потоки, он ссылается на потоки: подумайте ThreadLocal<T> как WeakHashMap<Thread, T>.

В вашем примере, если сборщик классов собран, он также выгрузит класс Test (если у вас нет утечки памяти), и будет собрано ThreadLocal a.

Ответ 3

ThreadLocal содержит ссылку на WeakHashMap, которая содержит пары ключ-значение

enter image description here

Ответ 4

из этого, кажется, что объекты, на которые ссылается переменная ThreadLocal, собираются мусором только тогда, когда поток умирает.

Это чрезмерное упрощение. Фактически это говорит о двух вещах:

  • Значение переменной не будет собираться мусором, пока поток жив (не был завершен), и объект ThreadLocal может достигать цели.

  • При завершении потока значение будет подчиняться нормальным правилам сбора мусора.

Существует важный третий случай, когда поток по-прежнему жив, но ThreadLocal уже недоступен. Это не распространяется на javadoc. Таким образом, поведение GC в этом случае не определено и может потенциально отличаться в разных реализациях Java.

Фактически, для OpenJDK Java 6 через OpenJDK Java 8 (и других реализаций, полученных из этих кодовых оснований) фактическое поведение довольно сложно. Значения нитей-нитей потока содержатся в объекте ThreadLocalMap. В комментариях сказано следующее:

ThreadLocalMap - это настроенная хеш-карта, подходящая только для поддержки локальных значений потоков. [...] Чтобы помочь справиться с очень большими и долговечными обычаями, записи хэш-таблицы используют WeakReferences для ключей. Однако, поскольку эталонные ссылки не используются, устаревшие записи гарантированно будут удалены, только когда таблица начнет работать вне пространства.

Если вы посмотрите на код, устаревшие записи карты (со сломанным WeakReferences) также могут быть удалены при других обстоятельствах. Если черная запись встречается в операции get, set, insert или remove на карте, соответствующее значение будет нулевым. В некоторых случаях код выполняет эвристику частичного сканирования, но единственная ситуация, когда мы можем гарантировать удаление всех устаревших записей карты, - это когда размер хэш-таблицы изменяется (растет).


Итак...

Затем во время контекста undeploy приложение classloader становится объектом сбора мусора, но поток из пула потоков, поэтому он не умирает. Будет ли объект b объектом сбора мусора?

Самое лучшее, что мы можем сказать, это то, что это может быть... в зависимости от того, как приложение управляет другими локалями потоков в рассматриваемом потоке.

Итак, устаревшие записи на основе локальной таблицы могут быть утечкой хранилища, если вы повторно развертываете webapp, если веб-контейнер не уничтожает и не воссоздает все потоки запросов в пуле потоков. (Но я бы ожидал, что веб-контейнер сделает это...)

Ответ 5

Это зависит не от сбора мусора, если вы ссылаетесь на него как на статичный или одиночный, а ваш класс не выгружается, поэтому в среде сервера приложений и с значениями ThreadLocal вам нужно использовать какой-либо прослушиватель или запросите фильтр, убедитесь, что вы разыскиваете все локальные переменные потока в конце обработки запроса. Или либо используйте некоторые функции области запроса вашей инфраструктуры.

Вы можете посмотреть здесь для некоторых других объяснений.

РЕДАКТИРОВАТЬ: В контексте пула потоков, как было задано, конечно, если поток - это мусорные локаторы потока.

Ответ 6

Объект b не будет зависеть от сбора мусора, если он каким-то образом относится к вашему классу Test. Это может произойти без вашего намерения. Например, если у вас есть такой код:

public class Test {
    private static final ThreadLocal<Set<Integer>> a =
     new ThreadLocal<Set<Integer>>(){
            @Override public Set<Integer> initialValue(){
                return new HashSet<Integer>(){{add(5);}};
            }
    };
}

Инициализация двойной скобки {{add (5);}} создаст анонимный класс, который относится к вашему классу Test, чтобы этот объект никогда не собирался собирать мусор, даже если у вас больше нет ссылки на ваш тестовый класс. Если этот тестовый класс используется в веб-приложении, он будет ссылаться на его загрузчик классов, который будет препятствовать тому, чтобы все другие классы были GCed.

Кроме того, если ваш объект b является простым объектом, он не будет немедленно подвергнут GC. Только когда размер ThreadLocal.ThreadLocalMap в классе Thread будет изменен, вы получите объект b для GC.

Однако я создал решение для этой проблемы, поэтому при повторном развертывании вашего веб-приложения у вас никогда не будет утечек загрузчика класса.