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

Как очистить threadlocals

Есть ли у кого-нибудь пример, как это сделать? или они обрабатываются сборщиком мусора? im, используя tomcat6

4b9b3361

Ответ 1

В javadoc сказано следующее:

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

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

Есть две причины, по которым вы можете очистить локаторы потоков для потоков в пуле потоков:

  • для предотвращения утечки памяти (или гипотетического ресурса) или
  • чтобы предотвратить случайную утечку информации из одного запроса другому через локаторы потоков.

Неполадки локальной локальной памяти обычно не должны быть серьезной проблемой при ограниченных пулах потоков, поскольку в любом случае локаторы потоков могут быть переписаны; т.е. когда поток повторно используется. Однако, если вы допустили ошибку при создании новых экземпляров ThreadLocal снова и снова (вместо использования переменной static для хранения экземпляра singleton), локальные значения потока не будут перезаписаны и будут накапливаться в каждом нить threadlocals. Это может привести к серьезной утечке.


Предполагая, что вы говорите о локалях потоков, которые создаются/используются во время обработки веб-запроса HTTP-запроса, тогда один из способов избежать локальных утечек потока - зарегистрировать ServletRequestListener с помощью вашего webapp ServletContext и реализовать listener requestDestroyed, чтобы очистить локаторы потоков для текущего потока.

Обратите внимание, что в этом контексте вам также необходимо рассмотреть возможность утечки информации из одного запроса в другой.

Ответ 2

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

    private void cleanThreadLocals() {
        try {
            // Get a reference to the thread locals table of the current thread
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalTable = threadLocalsField.get(thread);

            // Get a reference to the array holding the thread local variables inside the
            // ThreadLocalMap of the current thread
            Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(threadLocalTable);

            // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
            // is a reference to the actual ThreadLocal variable
            Field referentField = Reference.class.getDeclaredField("referent");
            referentField.setAccessible(true);

            for (int i=0; i < Array.getLength(table); i++) {
                // Each entry in the table array of ThreadLocalMap is an Entry object
                // representing the thread local reference and its value
                Object entry = Array.get(table, i);
                if (entry != null) {
                    // Get a reference to the thread local object and remove it from the table
                    ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
                    threadLocal.remove();
                }
            }
        } catch(Exception e) {
            // We will tolerate an exception here and just log it
            throw new IllegalStateException(e);
        }
    }

Ответ 3

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

Хорошее место для такой очистки - ServletRequestListener.requestDestroyed().

Если вы используете Spring, все необходимые проводки уже установлены, вы можете просто поместить вещи в область запроса, не беспокоясь о их очистке (что происходит автоматически):

RequestContextHolder.getRequestAttributes().setAttribute("myAttr", myAttr, RequestAttributes.SCOPE_REQUEST);
. . .
RequestContextHolder.getRequestAttributes().getAttribute("myAttr", RequestAttributes.SCOPE_REQUEST);

Ответ 4

Я хотел бы внести свой ответ на этот вопрос, даже если он старый. Я столкнулся с одной и той же проблемой (gson threadlocal не удалялся из потока запросов), и даже получил удобную перезагрузку сервера в любое время, когда у него закончилась память (что затягивает большое время!).

В контексте веб-приложения java, установленного в режиме dev (при этом сервер настроен на отказывание каждый раз, когда он воспринимает изменение кода и, возможно, также работает в режиме отладки), я быстро узнал, что threadlocals может быть удивительным и когда-нибудь быть болью. Я использовал threadlocal Invocation для каждого запроса. Внутри Invocation. Иногда я также использовал gson для генерации моего ответа. Я бы обернул Invocation внутри блока "try" в фильтре и уничтожил его внутри блока "finally".

То, что я наблюдал (на данный момент у меня нет показателей для резервного копирования), я добавил, что если я внес изменения в несколько файлов, и сервер постоянно подпрыгивал между моими изменениями, я бы стал нетерпеливым и перезапустил сервер (tomcat to быть точным) из IDE. Скорее всего, я бы закончил исключение "Out of memory".

Как я обошел это, нужно включить в приложение приложение ServletRequestListener, и моя проблема исчезла. Я думаю, что происходит то, что в середине запроса, если сервер будет отказываться несколько раз, мои threadlocals не очищаются (включая gson), поэтому я получаю это предупреждение о threadlocals и двух или трех предупреждениях позже, сервер сбой. Когда ServletResponseListener явно закрывает мои threadlocals, проблема gson исчезла.

Надеюсь, что это имеет смысл и дает вам представление о том, как преодолеть проблемы, связанные с threadlocal. Всегда закрывайте их вокруг своей точки использования. В ServletRequestListener проверьте каждую оболочку threadlocal и, если она все еще имеет действительную ссылку на какой-либо объект, уничтожьте ее в этой точке.

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

Ответ 5

JVM автоматически очистит все объекты без ссылки, которые находятся в объекте ThreadLocal.

Другой способ очистки этих объектов (скажем, например, эти объекты могут быть все небезопасные объекты потока, которые существуют вокруг) заключается в том, чтобы поместить их внутри некоторого класса Object Holder, который в основном удерживает его, и вы можете переопределить метод finalize очистите объект, который находится внутри него. Опять же, это зависит от сборщика мусора и его политик, когда он будет ссылаться на метод finalize.

Вот пример кода:

public class MyObjectHolder {

    private MyObject myObject;

    public MyObjectHolder(MyObject myObj) {
        myObject = myObj;
    }

    public MyObject getMyObject() {
        return myObject;
    }

    protected void finalize() throws Throwable {
        myObject.cleanItUp();
    }
}

public class SomeOtherClass {
    static ThreadLocal<MyObjectHolder> threadLocal = new ThreadLocal<MyObjectHolder>();
    .
    .
    .
}

Ответ 6

Внимательно прочитайте документацию Javadoc:

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

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

Локальная нить не может быть причиной утечки памяти, поскольку реализация соответствует документации.