Утечка локальных ресурсов и слабая ссылка - PullRequest
15 голосов
/ 02 июня 2009

Мое ограниченное понимание ThreadLocal состоит в том, что у него проблемы утечки ресурсов . Я понимаю, что эту проблему можно исправить путем правильного использования WeakReferences с ThreadLocal (хотя я, возможно, неправильно понял этот пункт.) Я просто хотел бы использовать шаблон или пример для правильного использования ThreadLocal с WeakReference, если таковой существует. Например, в этом фрагменте кода, где будет представлена ​​WeakReference?

static class DateTimeFormatter {
    private static final ThreadLocal<SimpleDateFormat> DATE_PARSER_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy/MM/dd HH:mmz");
        }
    };
    public String format(final Date date) {
        return DATE_PARSER_THREAD_LOCAL.get().format(date);
    }
    public Date parse(final String date) throws ParseException
    {
      return DATE_PARSER_THREAD_LOCAL.get().parse(date);
    }
}

Ответы [ 7 ]

31 голосов
/ 02 июня 2009

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

Кроме того, значения ThreadLocal фактически сохраняются в Thread; если поток умирает, все значения, связанные с этим потоком через ThreadLocal, собираются.

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


Обновление: Упомянутая проблема вступает в игру только тогда, когда значение, сохраненное в ThreadLocal, строго указывает на то, что ThreadLocal - своего рода круговая ссылка.

В этом случае значение (a SimpleDateFormat) не имеет обратной ссылки на ThreadLocal. В этом коде нет утечки памяти.

10 голосов
/ 02 июня 2009

Я предполагаю, что вы перепрыгиваете через эти обручи, поскольку SimpleDateFormat не является поточно-ориентированным .

Хотя я знаю, что я не решаю вашу проблему выше, могу ли я предложить вам посмотреть Joda для вашей работы с датой / временем? Joda имеет поточно-ориентированный механизм форматирования даты / времени. Вы также не будете тратить свое время на изучение API Joda, так как это основа для нового стандартного предложения API даты и времени.

3 голосов
/ 02 июня 2009

Там не должно быть такой проблемой.

Ссылка на ThreadLocal для потока определена как существующая только до тех пор, пока соответствующий поток является живым (см. Javadoc), или, другими словами, если поток не существует, если ThreadLocal была единственной ссылкой на этот объект, тогда объект становится пригодным для сборки мусора.

Так что либо вы обнаружили настоящую ошибку и должны сообщить об этом, либо вы делаете что-то еще неправильно!

2 голосов
/ 02 июня 2009

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

Предложения:

  1. Используйте фильтр аналогичной оболочки для каждого взаимодействия, чтобы очистить ThreadLocal в конце каждого взаимодействия
  2. Вы можете использовать альтернативу SimpleDateFormat, например FastDateFormat от commons-lang или Joda, как кто-то уже предложил
  3. Просто создайте новый SimpleDateFormat каждый раз, когда вам это нужно. Я знаю, что это расточительно, но в большинстве приложений вы просто не заметите разницу
1 голос
/ 02 июня 2009

Просто добавьте к сказанному @Neil Coffey, это не должно быть проблемой, если ваш экземпляр ThreadLocal статичен. Поскольку вы продолжаете вызывать get () для статического экземпляра, он всегда должен содержать одну и ту же ссылку на ваш простой форматер даты. Поэтому, как сказал Нил, когда поток завершается, единственный экземпляр простого средства форматирования даты должен иметь право на сборку мусора.

Если у вас есть числа или какая-либо другая форма отладки, которая показывает, что это представляет проблемы с ресурсами, то это уже другая история. Но я считаю, что это не должно быть проблемой.

0 голосов
/ 23 декабря 2013

очистите поток локально после его использования, добавьте фильтр сервлетов для этого:

protected ThreadLocal myThreadLocal = new ThreadLocal();

public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException {
    try {
        // Set ThreadLocal (eg. to store heavy computation result done only once per request)
        myThreadLocal.set(context());

        chain.doFilter(req, res);
    } finally {
        // Important : cleanup ThreaLocal to prevent memory leak
        userIsSmartTL.remove();
    }
}
0 голосов
/ 02 июня 2009

В вашем примере не должно быть проблем с использованием ThreadLocal вообще.

Локальные потоки (и синглтоны также!) Становятся проблемой, когда локальные потоки настроены на экземпляры, загруженные загрузчиком классов для последующей загрузки. Типичная ситуация в контейнере сервлета (например, tomcat):

  1. Веб-приложение устанавливает локальный поток во время обработки запроса.
  2. Поток управляется контейнером.
  3. Веб-приложение должно быть отключено.
  4. Загрузчик классов веб-приложения не может быть обработан, поскольку осталась ссылка: от локального потока к экземпляру до его класса и к загрузчику классов.

То же самое с синглетами (драйверы java.sql.DriverManger и JDBC, предлагаемые веб-приложением).

Так что избегайте таких вещей, особенно в условиях, не находящихся под вашим полным контролем!

...