Collecte des ordures ménagères ThreadLocal


De javadoc

Chaque thread contient une référence implicite à sa copie d'une variable thread-local tant que le thread est vivant et que l'instance ThreadLocal est accessible; après le départ d'un thread, toutes ses copies d'instances thread-local sont soumises à la récupération de place (sauf si d'autres références à ces copies existent).

Il semble que les objets référencés par une variable ThreadLocal ne soient collectés que lorsque le thread meurt. Mais que se passe - t-il si la variable ThreadLocal a n'est plus référencée et est soumise à la récupération de place? Les références d'objets uniquement par variable a seront-elles soumises à la récupération de place si le thread qui contient a est toujours vivant?

Par exemple, il y a la classe suivante avec la variable ThreadLocal:

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

Cette classe fait référence à un objet et cet objet n'a pas d'autres références. Ensuite, pendant le contexte undeploy application classloader devient un sujet pour la récupération de place, mais le thread provient d'un pool de threads, il ne meurt donc pas. L'objet b sera-t-il sujet à la collecte des ordures?

Author: michael nesterenko, 2013-06-14

6 answers

Les variables ThreadLocal sont maintenues dans le thread

ThreadLocal.ThreadLocalMap threadLocals;

Qui est initialisé paresseusement lors de la première invocation ThreadLocal.set/get dans le thread actuel et contient une référence au map jusqu'à ce que Thread soit vivant. Cependant, ThreadLocalMap utilise WeakReferences pour les clés, de sorte que ses entrées peuvent être supprimées lorsque ThreadLocal est référencé de nulle part ailleurs. Voir ThreadLocal.ThreadLocalMap javadoc pour plus de détails

 6
Author: Evgeniy Dorofeev, 2018-05-28 08:31:45

Si le ThreadLocal lui-même est collecté parce qu'il n'est plus accessible (il y a un " et " dans la citation), alors tout son contenu peut éventuellement être collecté, selon qu'il est également référencé ailleurs et d'autres ThreadLocal manipulations se produisent sur le même thread, déclenchant la suppression des entrées périmées (voir par exemple replaceStaleEntry ou expungeStaleEntry méthodes dans ThreadLocalMap). Le ThreadLocal n'est pas (fortement) référencées par, le fils, il références le fils: pensez à ThreadLocal<T> comme WeakHashMap<Thread, T>.

Dans votre exemple, si le classloader est collecté, il déchargera également la classe Test (sauf si vous avez une fuite de mémoire), et le ThreadLocal a seront collectées.

 6
Author: Frank Pavageau, 2014-12-16 22:09:27

De cela, il semble que les objets référencés par une variable ThreadLocal ne soient collectés que lorsque le thread meurt.

C'est une simplification excessive. Ce qu'il dit en fait est deux choses:

  • La valeur de la variable ne sera pas récupérée tant que le thread est vivant (ne s'est pas terminé) ET QUE l'objet ThreadLocal est fortement accessible.

  • La valeur sera soumise à des règles de récupération de place normales lorsque le thread se termine.

Il y a un troisième cas important où le thread est toujours en direct mais le ThreadLocal n'est plus fortement accessible. Cela n'est pas couvert par le javadoc. Ainsi, le comportement du GC dans ce cas n'est pas spécifié et pourrait potentiellement être différent entre différentes implémentations Java.

En fait, pour OpenJDK Java 6 à OpenJDK Java 8 (et d'autres implémentations dérivées de ces bases de code), le comportement réel est plutôt compliqué. Le valuas de les thread-locals d'un thread sont conservés dans un objet ThreadLocalMap. Les commentaires disent ceci:

ThreadLocalMap est une carte de hachage personnalisée adaptée uniquement pour maintenir les valeurs locales des threads. [...] Pour aider à traiter les utilisations très grandes et de longue durée, les entrées de la table de hachage utilisent WeakReferences pour les clés. Cependant, étant donné que les files d'attente de référence ne sont pas utilisées, les entrées périmées sont garanties d'être supprimées uniquement lorsque la table commence à manquer d'espace.

Si vous regardez le code, les entrées de carte périmées (avec {[4 cassé]}) peut également être retiré dans d'autres circonstances. Si une entrée périmée est rencontrée dans une opération get, set, insert ou remove sur la carte, la valeur correspondante est annulée. Dans certains cas, le code effectue une heuristique d'analyse partielle, mais la seule situation où nous pouvons garantir que toutes les entrées de carte périmées sont supprimées est lorsque la table de hachage est redimensionnée (grandit).


Donc ...

Puis pendant le contexte undeploy application classloader devient un sujet pour garbage collection, mais le thread provient d'un pool de threads, il ne meurt donc pas. L'objet b sera-t-il sujet à la collecte des ordures?

Le mieux que nous puissions dire est que cela peut être ... selon la façon dont l'application gère d'autres sections locales de thread, le thread en question.

Donc oui, les entrées de carte locale de thread périmées pourraient être une fuite de stockage si vous redéployer une webapp, sauf si le conteneur Web détruit et recrée tous les threads de requête dans le pool de threads. (Mais je m'attendrais à un web conteneur pour le faire ...)

 6
Author: Stephen C, 2014-12-17 12:40:18

ThreadLocal contient une référence à un WeakHashMap qui contient des paires clé-valeur

entrez la description de l'image ici

 3
Author: anish, 2013-06-14 10:00:19

Cela dépend, il ne sera pas collecté si vous le référencez comme statique ou par singleton et que votre classe n'est pas déchargée, c'est pourquoi dans l'environnement du serveur d'applications et avec des valeurs ThreadLocal, vous devez utiliser un filtre d'écoute ou de demande.assurez-vous que vous déréférencez toutes les variables locales de thread à la fin Ou utilisez une fonctionnalité Request scope de votre framework.

, Vous pouvez regarder ici pour certains autres explication.

EDIT: Dans le contexte d'un pool de threads comme demandé, bien sûr, si le thread est garbaged, les locaux de thread le sont.

 0
Author: gma, 2017-05-23 12:17:05

L'objet b ne sera pas soumis à la récupération de place s'il fait en quelque sorte référence à votre classe de test. Cela peut arriver sans votre intention. Par exemple, si vous avez un code comme ceci:

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);}};
            }
    };
}

L'initialisation à double accolade {{add(5);}} créera une classe anonyme qui fait référence à votre classe de test afin que cet objet ne soit jamais récupéré même si vous n'avez plus de référence à votre classe de test. Si cette classe de test est utilisée dans une application Web elle fera référence à son chargeur de classe qui empêchera toutes les autres classes d'être GCed.

De plus, si votre objet b est un objet simple, il ne sera pas immédiatement soumis à GC. Seulement quand ThreadLocal.ThreadLocalMap dans la classe Thread est redimensionné, vous aurez votre objet b subject pour GC.

Cependant, j'ai créé une solution pour ce problème, donc lorsque vous redéployez votre application Web, vous n'aurez jamais de fuites de chargeur de classe.

 -1
Author: user1944408, 2016-08-09 12:58:27