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

Threadlocal удалить?

При использовании ThreadLocal должен ли я всегда вызывать remove(), когда это делается или когда я делаю set, старое значение заменяется так или иначе remove является избыточным?

4b9b3361

Ответ 1

Поскольку ThreadLocal имеет Map из currentThread и value, теперь, если вы не удаляете значение в потоке, который его использовал, это создаст утечку памяти.

Вы всегда должны вызывать удалить, потому что класс ThreadLocal помещает значения из класса Тема, определенного ThreadLocal.Values ​​localValues; Это также приведет к сохранению ссылки на Thread и связанные с ним объекты.

Из исходного кода ThreadLocal

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

Ответ 2

set всегда заменяет старое значение.

Это верно для

  • Calendar.set() и Date.set()
  • BitSet.set()
  • List.set()
  • сеттеров

Вы имеете в виду, что без удаления он не будет GCed?

Он не будет удален, пока нить не погибнет. Он не исчезнет на вас, если вы не вызовете remove()

Является ли это утечкой памяти или нет, зависит от вашей программы. Вам нужно было бы создать много потоков с большими потоковыми локальными объектами, которые по какой-то причине вам не нужны. например 1000 потоков с объектом 1 КБ могут сбрасывать до 1 МБ, но это предполагает проблему дизайна, если вы делаете такие вещи.


Единственное место, где вы можете получить утечку памяти, - это.

for (int i = 0; ; i++) {
    // don't subclass Thread.
    new Thread() {
        // this is somewhat pointless as you are defining a ThreadLocal per thread.
        final ThreadLocal<Object> tlObject = new ThreadLocal<Object>() {
        };

        public void run() {
            tlObject.set(new byte[8 * 1024 * 1024]);
        }
    }.start();
    Thread.sleep(1);
    if (i % 1000 == 0) {
        System.gc();
        System.out.println(i);
    }
}

с отпечатками -verbosegc.

[Full GC 213548K->49484K(3832192K), 0.0334194 secs]
39000
[GC 2786060K->82412K(3836864K), 0.0132035 secs]
[GC 2815569K->107052K(3836544K), 0.0212252 secs]
[GC 2836162K->131628K(3837824K), 0.0199268 secs]
[GC 2867613K->156204K(3837568K), 0.0209828 secs]
[GC 2886894K->180780K(3838272K), 0.0191244 secs]
[GC 2911942K->205356K(3838080K), 0.0187482 secs]
[GC 421535K->229932K(3838208K), 0.0192605 secs]
[Full GC 229932K->49484K(3838208K), 0.0344509 secs]
40000

Примечание: размер после полного GC является тем же самым 49484K

В приведенном выше случае вы будете иметь ThreadLocal, который ссылается на Thread, который ссылается на ThreadLocal. Однако, когда поток мертв, он не вызывает утечку памяти, потому что он становится объектом учета, т.е. Когда A → B и B → A

Я запустил приведенный выше пример в цикле в течение нескольких минут, а уровни GC перемещались много, но минимальный размер был все еще небольшим.

Ответ 3

set: устанавливает текущую копию потока этой локальной переменной потока в указанное значение.

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

Ответ 4

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

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

Ответ 5

Я сделаю это просто: Если вы расширяете ThreadLocal по любой причине, используйте remove(). На ваниле ThreadLocal используйте set(null). В принципе, не используя ThreadLocal.remove() в расширенном ThreadLocal может привести к утечкам памяти (скорее всего, ClassLoader)

Если вам нужна дополнительная информация, отправьте комментарий.

Ответ 6

Нет, вам не нужно "всегда вызывать remove()" вместо set()

Если вы боитесь утечек памяти при этом, вот что говорит Javadoc

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

Таким образом, отсутствие вызова remove() не помешает правильной сборке мусора локальному экземпляру потока и не вызовет утечек памяти по своей природе.

Вы также можете взглянуть на реализацию ThreadLocal, которая использует WeakReferences для этого механизма "неявной ссылки".

Но остерегайтесь согласованности с пулами потоков

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

Ответ 7

Если поток завершен, threadLocalMap будет выполнен с потоком. Вам не нужно удалять его. Но если в потоке используется вторичная переработка, вам необходимо удалить значение threadLocalMap.