Проблема с утечкой памяти ThreadLocal

сервер контейнер Tomcat
I. Обзор

Класс ThreadLocal используется для предоставления локальных переменных в потоке. При доступе к этим переменным в многопоточной среде (доступ с помощью методов get или set) можно гарантировать относительную независимость переменных в каждом потоке от переменных в других потоках.Экземпляры ThreadLocal обычно являются закрытыми статическими типами. Резюме: ThreadLocal предназначен не для решения многопоточного доступа к общим переменным, а для создания отдельной копии переменной для каждого потока, предоставляя способ сохранить объект и избежать сложности передачи параметров.

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


2. Принцип

ThreadLocal можно рассматривать как контейнер, в котором хранятся переменные, принадлежащие текущему потоку. Класс ThreadLocal предоставляет четыре открытых метода интерфейса, которые также являются основными методами для пользователей, работающих с классом ThreadLocal:

(1) void set(значение объекта) Устанавливает значение локальной переменной текущего потока.

(2) public Object get() Этот метод возвращает локальную переменную потока, соответствующую текущему потоку. 

(3) public void remove() удаляет значение локальной переменной текущего потока, чтобы уменьшить занимаемую память, этот метод является новым методом в JDK 5.0. Следует отметить, что когда поток завершается, локальные переменные, соответствующие потоку, будут автоматически удалены сборщиком мусора, поэтому нет необходимости явно вызывать этот метод для очистки локальных переменных потока, но он может ускорить скорость. восстановления памяти. 

(4) protected Object initialValue() возвращает начальное значение локальной переменной потока Этот метод является защищенным методом, который, очевидно, предназначен для переопределения подклассами. Этот метод представляет собой метод отложенного вызова, который выполняется, когда поток вызывает get() или set(Object) в первый раз, и выполняется только один раз Реализация по умолчанию в ThreadLocal напрямую возвращает значение null. Вышеупомянутые методы могут использоваться для доступа к переменным в ThreadLocal, установки данных, инициализации и удаления локальных переменных.Как ThreadLocal поддерживает копии переменных для каждого потока? На самом деле в классе ThreadLocal есть статический внутренний класс ThreadLocalMap (похожий на Map), который хранит переменную копию каждого потока в виде пар ключ-значение.Ключом элемента в ThreadLocalMap является текущий объект ThreadLocal, и значение соответствует копии переменной потока.В каждом потоке может быть несколько ThreadLocals.


3. Проблема с утечкой памяти

В каждом потоке есть карта, и тип карты — ThreadLocal.ThreadLocalMap.Ключ на карте - это экземпляр threadlocal. Эта карта использует слабую ссылку, но слабая ссылка предназначена только для ключа. Каждый ключ имеет слабую ссылку на threadlocal. Когда для экземпляра threadlocal задано значение null, сильной ссылки на экземпляр threadlocal нет, поэтому будет возвращен gc. Однако наше значение не может быть переработано, потому что есть сильная ссылка, связанная с текущим потоком. Только после того, как текущий поток завершится, текущий поток не будет существовать в стеке, сильная ссылка будет отключена, и текущий поток, карта и значение будут удалены. Таким образом, вывод состоит в том, что до тех пор, пока объект потока перерабатывается gc, утечки памяти не будет, но когда для threadLocal установлено значение null и поток завершается, он не будет переработан, и мы думаем, что это утечка памяти. На самом деле это нестыковка в понимании понятия, и спорить тут не о чем. Самое ужасное, что объект потока не перерабатывается, что является настоящей утечкой памяти.Например, при использовании пула потоков поток не будет уничтожен, когда он завершится, и при повторном использовании могут возникнуть утечки памяти. (В веб-приложении каждый HTTP-запрос представляет собой поток, и проблема утечки памяти возникает, когда контейнер tomcat настроен на использование пула потоков)


ThreadLocal работает только с ThreadLocalMap в Thread. Каждый Thread имеет карту. ThreadLocalMap — это внутренний атрибут потока. Жизненный цикл ThreadLocalMap такой же, как и у Thread, и не зависит от ThreadMap.

ThreadLocal хранится в карте через Entry, ключ — это слабая ссылка Thread (которая будет автоматически переработана при сборке мусора), а значение — копия хранимой переменной. переменные через ThreadLocalMap, который может быть Как видно из исходного кода, объект карты получается первым при установке значения. Если он не существует, он будет создан. Начальный размер threadLocalMap равен 16, и он будет автоматически расширяться когда емкость превышает 2/3.


4. Резюме

1. Используйте ThreadLocal, рекомендуется использовать статическую модификацию static ThreadLocal headerLocal = new ThreadLocal();

2. После использования ThreadLocal выполните операцию удаления, чтобы избежать переполнения памяти.