Comment nettoyer les threadlocals

Est-ce que quelqu’un a un exemple comment faire cela? ou sont-ils manipulés par le ramasse-miettes? im en utilisant tomcat6

Le javadoc dit ceci:

“Chaque thread contient une référence implicite à sa copie d’une variable thread-locale tant que le thread est actif et que l’instance ThreadLocal est accessible. Après la disparition d’un thread, toutes ses copies d’instances thread-locales sont soumises à une récupération de mémoire (sauf s’il existe d’autres références à ces copies).

Si votre application ou (si vous parlez de threads de requête) utilise un pool de threads, cela signifie que les threads ne meurent pas. Si nécessaire, vous devrez vous occuper vous-même des sections locales du thread. La seule façon propre de le faire est d’appeler la méthode ThreadLocal.remove() .

Il y a deux raisons pour lesquelles vous pourriez vouloir nettoyer les sections de thread pour les threads dans un pool de threads:

  • pour empêcher les memory leaks (ou de ressources hypothétiques), ou
  • pour éviter les fuites accidentelles d’informations d’une requête à une autre via les localisations de threads.

Les memory leaks locale des threads ne devraient normalement pas être un problème majeur avec les pools de threads bornés, car toutes les sections de thread sont susceptibles d’être écrasées éventuellement; c’est-à-dire lorsque le fil est réutilisé. Cependant, si vous faites l’erreur de créer ThreadLocal instances de ThreadLocal (au lieu d’utiliser une variable static pour contenir une instance de singleton), les valeurs locales du thread ne seront pas écrasées et s’accumuleront dans la carte des threadlocals chaque thread. Cela pourrait entraîner une fuite grave.


En supposant que vous parlez de sections locales de thread créées / utilisées lors du traitement d’une requête HTTP par une application ServletRequestListener vous pouvez enregistrer une ServletRequestListener avec ServletRequestListener de votre application ServletContext et implémenter la méthode requestDestroyed l’écouteur pour requestDestroyed le thread. locaux pour le fil actuel.

Notez que dans ce contexte, vous devez également tenir compte de la possibilité de fuite d’ informations d’une requête à une autre.

Voici un code pour nettoyer toutes les variables locales de thread du thread en cours lorsque vous n’avez pas de référence à la variable locale de thread réelle. Vous pouvez également le généraliser pour nettoyer les variables locales des threads pour les autres threads:

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

Il n’y a aucun moyen de ThreadLocal valeurs ThreadLocal , sauf dans le thread qui les a installées en premier lieu (ou lorsque le thread est nettoyé – pas le cas avec les threads de travail). Cela signifie que vous devez prendre soin de nettoyer votre ThreadLocal lorsqu’une requête de servlet est terminée (ou avant de transférer AsyncContext sur un autre thread dans Servlet 3), car après cela, vous ne pourrez plus jamais entrer ce thread de travail spécifique. perdra de la mémoire lorsque votre application Web n’est pas déployée alors que le serveur n’est pas redémarré.

ServletRequestListener.requestDestroyed () est un bon endroit pour effectuer ce nettoyage.

Si vous utilisez Spring, tout le câblage nécessaire est déjà en place, vous pouvez simplement placer des éléments dans le champ de votre requête sans vous soucier de les nettoyer (cela se produit automatiquement):

 RequestContextHolder.getRequestAtsortingbutes().setAtsortingbute("myAttr", myAttr, RequestAtsortingbutes.SCOPE_REQUEST); . . . RequestContextHolder.getRequestAtsortingbutes().getAtsortingbute("myAttr", RequestAtsortingbutes.SCOPE_REQUEST); 

Relire la documentation de Javadoc attentivement:

«Chaque thread contient une référence implicite à sa copie d’une variable thread-locale tant que le thread est actif et que l’instance ThreadLocal est accessible. une fois qu’un thread disparaît, toutes ses copies d’instances locales de thread sont soumises à la récupération de la mémoire (sauf s’il existe d’autres références à ces copies). ‘

Il n’y a pas besoin de nettoyer quoi que ce soit, il y a une condition “ET” pour que la fuite puisse survivre. Donc, même dans un conteneur Web où le thread survit à l’application, tant que la classe webapp est déchargée (seule la référence dans une classe statique chargée dans le chargeur de classe parent empêcherait cela et cela n’a rien à voir avec ThreadLocal mais les problèmes généraux avec les jars partagés avec des données statiques) alors la deuxième étape de la condition AND n’est plus satisfaite, la copie locale du thread est donc éligible pour le nettoyage de la mémoire.

Le thread local ne peut pas être à l’origine de memory leaks, dans la mesure où la mise en œuvre respecte la documentation.

Je voudrais apporter ma réponse à cette question même si elle est ancienne. J’avais été en proie au même problème (gson threadlocal ne pas être retiré du thread de requête), et j’avais même été à l’aise de redémarrer le serveur chaque fois qu’il manquait de mémoire (ce qui est énorme !!).

Dans le contexte d’une application Web Java configurée en mode dev (dans laquelle le serveur est configuré pour rebondir chaque fois qu’il détecte une modification du code, et peut-être aussi en mode débogage), j’ai rapidement compris que les threadlocals pouvaient être géniaux et parfois être une douleur. J’utilisais une Invocation locale pour chaque requête. À l’intérieur de l’invocation. J’utilise parfois aussi gson pour générer ma réponse. J’emballerais l’Invocation dans un bloc ‘try’ dans le filtre et le détruirais dans un bloc ‘finally’.

Ce que j’ai observé (je n’ai pas de mésortingque pour le faire pour l’instant), c’est que si je modifiais plusieurs fichiers et que le serveur rebondissait constamment entre mes modifications, je serais impatient de redémarrer le serveur (tomcat pour être précis) de l’EDI. Très probablement, je me retrouverais avec une exception “Mémoire insuffisante”.

Comment j’ai réussi à contourner cela consistait à inclure une implémentation ServletRequestListener dans mon application, et mon problème a disparu. Je pense que ce qui se passait, c’est qu’au milieu d’une requête, si le serveur rebondissait plusieurs fois, mes threadlocals ne seraient pas effacés (gson inclus) donc je recevrais cet avertissement sur les threadlocals et deux ou trois avertissements plus tard, le serveur tomberait en panne. Avec le ServletResponseListener fermant explicitement mes threadlocals, le problème de gson a disparu.

J’espère que cela a du sens et vous donne une idée de la manière de surmonter les problèmes locaux. Toujours les fermer autour de leur point d’utilisation. Dans ServletRequestListener, testez chaque wrapper threadlocal et, s’il contient toujours une référence valide à un object, détruisez-le à ce stade.

Je devrais également souligner que l’habitude d’encapsuler un threadlocal en tant que variable statique à l’intérieur d’une classe. De cette façon, vous pouvez être assuré que, en le détruisant dans ServeltRequestListener, vous n’aurez plus à vous soucier des autres instances de la même classe.

La JVM nettoie automatiquement tous les objects sans référence qui se trouvent dans l’object ThreadLocal.

Une autre façon de nettoyer ces objects (par exemple, ces objects peuvent être tous les objects dangereux existants) consiste à les placer dans une classe Object Holder, qui la contient et vous pouvez remplacer la méthode finalize pour nettoyer l’object. qui y résident. Là encore, cela dépend du récupérateur de place et de ses règles, lorsqu’il appelle la méthode finalize .

Voici un exemple de code:

 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 threadLocal = new ThreadLocal(); . . . }