Est-ce une bonne pratique d’utiliser java.lang.Ssortingng.intern ()?

Le Javadoc sur Ssortingng.intern() ne donne pas beaucoup de détails. (En bref: il retourne une représentation canonique de la chaîne, permettant de comparer les chaînes internes en utilisant == )

  • Quand utiliser cette fonction en faveur de Ssortingng.equals() ?
  • Existe-t-il des effets secondaires non mentionnés dans le Javadoc, à savoir une optimisation plus ou moins importante par le compilateur JIT?
  • Y a-t-il d’autres utilisations de Ssortingng.intern() ?

Quand utiliser cette fonction en faveur de Ssortingng.equals ()

quand vous avez besoin de vitesse puisque vous pouvez comparer des chaînes par référence (== est plus rapide que égal)

Y at-il des effets secondaires non mentionnés dans le Javadoc?

Le principal inconvénient est que vous devez vous rappeler de vous assurer que vous effectuez réellement toutes les chaînes que vous allez comparer. Il est facile d’oublier de stagiaire () toutes les chaînes et vous pouvez obtenir des résultats confus. De plus, pour le bien de tous, assurez-vous de bien documenter que vous comptez sur les chaînes en cours d’internalisation.

Le deuxième inconvénient si vous décidez d’internaliser les chaînes est que la méthode intern () est relativement coûteuse. Il doit gérer le pool de chaînes uniques pour qu’il fasse pas mal de travail (même si la chaîne a déjà été internalisée). Donc, soyez prudent dans votre conception de code afin que vous puissiez, par exemple, intern () toutes les chaînes appropriées en entrée pour que vous n’ayez plus à vous en soucier.

(de JGuru)

Troisième inconvénient (Java 7 ou moins uniquement): les chaînes internes vivent dans l’espace PermGen, qui est généralement assez petit; vous pouvez exécuter dans une erreur OutOfMemoryError avec beaucoup d’espace de tas libre.

(de Michael Borgwardt)

Cela n’a (presque) rien à voir avec la comparaison de chaînes. L’internalisation de chaînes est destinée à économiser de la mémoire si vous avez plusieurs chaînes avec le même contenu dans votre application. En utilisant Ssortingng.intern() l’application n’aura qu’une seule instance à long terme et un effet secondaire est que vous pouvez effectuer une comparaison d’égalité de référence rapide au lieu d’une comparaison de chaîne ordinaire (mais cela n’est généralement pas conseillé car il est très facile de casser en oubliant de ne interner qu’une seule instance).

Ssortingng.intern() est définitivement Ssortingng.intern() modernes.
La mémoire suivante ne fonctionne JAMAIS à cause de l’activité du GC:

 // java -cp . -Xmx128m UserOfIntern public class UserOfIntern { public static void main(Ssortingng[] args) { Random random = new Random(); System.out.println(random.nextLong()); while (true) { Ssortingng s = Ssortingng.valueOf(random.nextLong()); s = s.intern(); } } } 

Voir plus (de moi) sur le mythe de non GCed Ssortingng.intern () .

J’ai récemment écrit un article sur l’implémentation de Ssortingng.intern () dans Java 6, 7 et 8: Ssortingng.intern dans le pool de chaînes Java 6, 7 et 8 .

J’espère que cela devrait contenir suffisamment d’informations sur la situation actuelle avec le pool de chaînes en Java.

En un mot:

  • Évitez Ssortingng.intern() dans Java 6, car il va dans PermGen
  • Prefer Ssortingng.intern() dans Java 7 et Java 8: il utilise 4 à 5 fois moins de mémoire que votre propre pool d’objects
  • Assurez-vous de régler -XX:SsortingngTableSize (la valeur par défaut est probablement trop petite; définissez un nombre premier)

La comparaison de chaînes avec == est beaucoup plus rapide qu’avec des équations ()

5 Temps plus rapide, mais comme la comparaison de chaînes ne représente généralement qu’un faible pourcentage du temps d’exécution total d’une application, le gain global est beaucoup plus faible que cela et le gain final sera dilué à quelques pour cent.

Ssortingng.intern () éloigne la chaîne de Heap et la place dans PermGen

Les chaînes intériorisées sont placées dans une zone de stockage différente: Génération permanente qui est une zone de la machine virtuelle Java réservée aux objects non-utilisateurs, tels que les classes, les méthodes et d’autres objects JVM internes. La taille de cette zone est limitée et l’espace est bien plus précieux que le tas. Étant donné que cette zone est plus petite que Heap, il y a plus de chances d’utiliser tout l’espace et d’obtenir une exception OutOfMemoryException.

Ssortingng.intern () chaîne est collectée

Dans les nouvelles versions de JVM, les chaînes intériorisées sont également collectées lorsqu’elles ne sont référencées par aucun object.

En gardant à l’esprit le point 3 ci-dessus, vous pourriez déduire que Ssortingng intern () ne pourrait être utile que dans quelques situations où vous faites beaucoup de comparaisons de chaînes, mais il vaut mieux ne pas utiliser de chaîne interne si vous ne savez pas exactement ce que vous faites …

Je ne suis au courant d’aucun avantage, et s’il y en avait un, on pourrait penser qu’equals () utiliserait lui-même intern () en interne (ce qui n’est pas le cas).

Busting interne () mythes

Quand utiliser cette fonction en faveur de Ssortingng.equals ()

Étant donné qu’ils font des choses différentes, probablement jamais.

Interner des chaînes pour des raisons de performances afin que vous puissiez les comparer pour une égalité de référence ne sera bénéfique que si vous maintenez des références aux chaînes pendant un certain temps – les chaînes provenant des entrées utilisateur ou IO ne seront pas internées.

Cela signifie que dans votre application, vous recevez une entrée d’une source externe et la transforme en un object qui a une valeur sémantique – un identifiant dit – mais cet object a un type indiscernable des données brutes, et a des règles différentes utilise le.

Il est presque toujours préférable de créer un type UserId interné (il est facile de créer un mécanisme d’internement générique thread-safe) et d’agir comme un enum ouvert que de surcharger le type java.lang.Ssortingng avec une sémantique de référence s’il se trouve être un ID utilisateur.

De cette façon, vous ne comprenez pas si une chaîne particulière a été ou non internée, et vous pouvez encapsuler tout comportement supplémentaire requirejs dans l’énumération ouverte.

Existe-t-il des effets secondaires non mentionnés dans le Javadoc, à savoir une optimisation plus ou moins importante par le compilateur JIT?

Je ne connais pas le niveau JIT, mais le pool de chaînes de caractères prend directement en charge le bytecode , qui est implémenté comme par magie et avec une structure CONSTANT_Ssortingng_info dédiée (contrairement à la plupart des autres objects qui ont des représentations plus génériques).

JVMS

JVMS 7 5.1 dit :

Un littéral de chaîne est une référence à une instance de classe Ssortingng et est dérivé d’une structure CONSTANT_Ssortingng_info (§4.4.3) dans la représentation binary d’une classe ou d’une interface. La structure CONSTANT_Ssortingng_info donne la séquence de points de code Unicode constituant le littéral de chaîne.

Le langage de programmation Java requirejs que les littéraux de chaîne identiques (c’est-à-dire les littéraux contenant la même séquence de points de code) doivent faire référence à la même instance de classe Ssortingng (JLS §3.10.5). De plus, si la méthode Ssortingng.intern est appelée sur une chaîne quelconque, le résultat est une référence à la même instance de classe qui serait renvoyée si cette chaîne apparaissait comme un littéral. Ainsi, l’expression suivante doit avoir la valeur true:

 ("a" + "b" + "c").intern() == "abc" 

Pour dériver un littéral de chaîne, la machine virtuelle Java examine la séquence de points de code donnée par la structure CONSTANT_Ssortingng_info.

  • Si la méthode Ssortingng.intern a déjà été appelée sur une instance de classe Ssortingng contenant une séquence de points de code Unicode identique à celle donnée par la structure CONSTANT_Ssortingng_info, le résultat de la dérivation de littéral de chaîne est une référence à cette même instance de classe Ssortingng.

  • Sinon, une nouvelle instance de classe Ssortingng est créée contenant la séquence de points de code Unicode donnée par la structure CONSTANT_Ssortingng_info; une référence à cette instance de classe est le résultat d’une dérivation littérale de chaîne. Enfin, la méthode intern de la nouvelle instance Ssortingng est appelée.

Bytecode

Il est également instructif d’examiner l’implémentation du bytecode sur OpenJDK 7.

Si on décomstack:

 public class SsortingngPool { public static void main(Ssortingng[] args) { Ssortingng a = "abc"; Ssortingng b = "abc"; Ssortingng c = new Ssortingng("abc"); System.out.println(a); System.out.println(b); System.out.println(a == c); } } 

nous avons sur la réserve constante:

 #2 = Ssortingng #32 // abc [...] #32 = Utf8 abc 

et main :

  0: ldc #2 // Ssortingng abc 2: astore_1 3: ldc #2 // Ssortingng abc 5: astore_2 6: new #3 // class java/lang/Ssortingng 9: dup 10: ldc #2 // Ssortingng abc 12: invokespecial #4 // Method java/lang/Ssortingng."":(Ljava/lang/Ssortingng;)V 15: astore_3 16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 19: aload_1 20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 23: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 33: aload_1 34: aload_3 35: if_acmpne 42 38: iconst_1 39: goto 43 42: iconst_0 43: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 

Notez comment:

  • 0 et 3 : la même constante ldc #2 est chargée (les littéraux)
  • 12 : une nouvelle instance de chaîne est créée (avec l’argument #2 )
  • 35 : a et c sont comparés comme objects réguliers avec if_acmpne

La représentation de chaînes constantes est assez magique sur le bytecode:

  • il a une structure CONSTANT_Ssortingng_info dédiée, contrairement aux objects réguliers (par exemple new Ssortingng )
  • La structure pointe vers une structure CONSTANT_Utf8_info contenant les données. C’est la seule donnée nécessaire pour représenter la chaîne.

et la citation de JVMS ci-dessus semble dire que chaque fois que le Utf8 indiqué est le même, alors des instances identiques sont chargées par ldc .

J’ai fait des tests similaires pour les champs et:

  • static final Ssortingng s = "abc" pointe vers la table des constantes via l’ atsortingbut ConstantValue
  • les champs non finaux n’ont pas cet atsortingbut, mais peuvent toujours être initialisés avec ldc

Bonus : comparez cela au pool Integer , qui ne prend pas directement en charge le bytecode (c.-à-d. Aucun analogue CONSTANT_Ssortingng_info ).

J’examinerais la comparaison interne et == – au lieu des égaux seulement dans le cas où la comparaison des équations est un goulot d’étranglement dans les comparaisons multiples de chaîne. Il est très peu probable que cela aide avec un petit nombre de comparaisons, car intern () n’est pas gratuit. Après avoir interné agressivement les chaînes, vous trouverez que les appels à intern () sont de plus en plus lents.

Une sorte de fuite de mémoire peut provenir de l’utilisation de subSsortingng() lorsque le résultat est petit comparé à la chaîne source et que l’object a une longue durée de vie.

La solution normale consiste à utiliser new Ssortingng( s.subSsortingng(...)) mais lorsque vous avez une classe qui stocke le résultat d’un sous- subSsortingng(...) potentiel / probable subSsortingng(...) et n’a aucun contrôle sur l’appelant, vous pouvez considérer pour stocker le intern() des arguments Ssortingng transmis au constructeur. Cela libère le grand tampon potentiel.

Daniel Brückner a tout à fait raison. L’internalisation de chaînes est destinée à économiser de la mémoire (tas). Notre système dispose actuellement d’un hashmap géant pour contenir certaines données. Au fur et à mesure que le système évolue, le hashmap sera suffisamment grand pour rendre le tas hors mémoire (comme nous l’avons testé). En internant toutes les chaînes dupliquées de tous les objects dans le hashmap, cela nous permet d’économiser une quantité importante de tas.

Toujours dans Java 7, les chaînes internes ne vivent plus dans PermGen mais heap à la place. Donc, vous n’avez pas besoin de vous soucier de sa taille et oui, il se fait ramasser les ordures:

Dans JDK 7, les chaînes internes ne sont plus allouées dans la génération permanente du segment de mémoire Java, mais sont plutôt allouées dans la partie principale du segment de mémoire Java (appelées générations jeunes et anciennes), avec les autres objects créés par l’application. . Cette modification se traduira par plus de données résidant dans le segment de mémoire principal Java et moins de données dans la génération permanente, ce qui peut nécessiter un ajustement de la taille des segments. La plupart des applications ne verront que des différences relativement faibles dans l’utilisation du tas en raison de ce changement, mais les applications plus volumineuses qui chargent de nombreuses classes ou qui utilisent beaucoup la méthode Ssortingng.intern () verront des différences plus significatives.

L’internalisation de chaînes est utile dans le cas où la méthode equals() est souvent invoquée car la méthode equals() vérifie rapidement si les objects sont identiques au début de la méthode.

 if (this == anObject) { return true; } 

Cela se produit généralement lors d’une recherche dans une Collection bien que d’autres codes puissent également effectuer des vérifications d’égalité de chaîne.

Il y a toutefois un coût à payer pour effectuer un stage, j’ai réalisé un microbenchmark de code et constaté que le processus d’internalisation augmentait le temps d’exécution d’un facteur de 10.

Le meilleur endroit pour faire l’internement est généralement lorsque vous lisez des clés stockées en dehors du code, car les chaînes du code sont automatiquement internées. Cela se produirait normalement aux étapes d’initialisation de votre application afin d’empêcher la pénalité du premier utilisateur.

Un autre endroit où cela peut être fait est le traitement des entrées utilisateur qui pourraient être utilisées pour effectuer des recherches de clés. Cela se produit normalement dans votre processeur de requête, notez que les chaînes internes doivent être transmises.

En dehors de cela, il est inutile de faire de l’internement dans le rest du code, car cela n’apporte généralement aucun avantage.

Je voterais pour que cela ne vaille pas les problèmes de maintenance.

La plupart du temps, il n’y aura pas besoin, et aucun avantage en termes de performances, à moins que votre code ne fasse beaucoup de travail avec les sous-chaînes. Dans ce cas, la classe Ssortingng utilisera la chaîne d’origine plus un décalage pour économiser de la mémoire. Si votre code utilise beaucoup de sous-chaînes, alors je suppose que cela fera exploser vos besoins en mémoire.

http://kohlerm.blogspot.co.uk/2009/01/is-javalangssortingngintern-really-evil.html

affirme que Ssortingng.equals() utilise "==" pour comparer les objects Ssortingng avant, selon

http://www.codeinstructions.com/2009/01/busting-javalangssortingngintern-myths.html

il compare les longueurs de chaînes, puis le contenu.

(Soit dit en passant, les chaînes de code de produit dans un catalogue de vente peuvent être de même longueur – BIC0417 est un casque de sécurité de vélo, TIG0003 est un tigre mâle adulte vivant – vous avez probablement besoin de toutes sortes de licences pour en commander une. peut-être préférez-vous commander un casque de sécurité en même temps.)

Il semble donc que vous ayez l’avantage de remplacer vos chaînes par leur version intern() , mais vous obtenez la sécurité, la lisibilité et la conformité standard, sans utiliser “==” pour les équations equals() dans votre programmation. Et la plupart de ce que je vais dire dépend de ce qui est vrai, si c’est vrai.

Mais Ssortingng.equals() teste-t-il que vous lui avez passé une Ssortingng et non un autre object, avant d’utiliser "==" ? Je ne suis pas qualifié pour le dire, mais je suppose que non, car la plupart des opérations equals() seront essentiellement Ssortingng to Ssortingng, ce qui signifie que le test est presque toujours passé. En effet, donner la priorité à “==” dans Ssortingng.equals() implique que vous comparez fréquemment la chaîne au même object réel.

J’espère que personne n’est surpris que les lignes suivantes produisent un résultat de “faux”:

  Integer i = 1; System.out.println("1".equals(i)); 

Mais si vous changez i en i.toSsortingng() dans la deuxième ligne, c’est bien sûr true .

Parmi les lieux où vous pourriez espérer bénéficier de l’internat, il ya évidemment Set et Map . J’espère que les chaînes de caractères internes auront leurs codes de hachage mis en cache … Je pense que ce serait une exigence. Et j’espère que je ne viens pas de donner une idée qui pourrait me rapporter un million de dollars. 🙂

En ce qui concerne la mémoire, il est également évident que c’est une limite importante si votre volume de chaînes est important ou si vous souhaitez que la mémoire utilisée par votre code de programme soit très petite. Si votre volume de -distinct- Ssortingngs est très volumineux, il est peut-être temps d’envisager d’utiliser un code de programme de firebase database dédié pour les gérer et un serveur de firebase database distinct. De même, si vous pouvez améliorer un petit programme (qui doit s’exécuter simultanément dans 10 000 instances) en ne le stockant pas du tout.

Il est inutile de créer une nouvelle chaîne et de la supprimer immédiatement pour son substitut intern() , mais il n’ya pas d’alternative claire, sauf pour conserver la chaîne en double. Donc, le coût d’exécution est vraiment de chercher votre chaîne dans le pool interne, puis de laisser le ramasse-miettes éliminer l’original. Et si c’est un littéral de chaîne, alors ça vient déjà de toute façon.

Je me demande si un code de programme malveillant peut maltraiter intern() pour détecter si certaines références de Ssortingng et d’object existent déjà dans le pool intern() et, par conséquent, existent ailleurs dans la session Java, alors que cela ne devrait pas être connu. Mais cela ne serait possible que lorsque le code du programme est déjà utilisé de manière confiante, je suppose. Néanmoins, il est important de prendre en compte les bibliothèques tierces que vous incluez dans votre programme pour stocker et mémoriser vos numéros PIN ATM!

La vraie raison d’utiliser le stagiaire n’est pas la précédente. Vous pouvez l’utiliser après avoir rencontré une erreur de mémoire insuffisante. Beaucoup de chaînes dans un programme typique sont Ssortingng.subssortingng () d’une autre grosse chaîne [pensez à retirer un nom d’utilisateur d’un fichier xml de 100K. L’implémentation Java est celle-ci, la sous-chaîne contient une référence à la chaîne d’origine et le début + fin dans cette énorme chaîne. (La pensée derrière elle est une réutilisation de la même grosse chaîne)

Après 1000 gros fichiers, à partir desquels vous ne sauvegardez que 1000 noms abrégés, vous garderez en mémoire les 1000 fichiers entiers! Solution: dans ce scénario, utilisez simplement smallsubssortingng.intern ()

J’utilise intern pour économiser de la mémoire, je détiens une grande quantité de données Ssortingng en mémoire et le déplacement pour utiliser intern () a permis d’économiser énormément de mémoire. Malheureusement, bien qu’il utilise beaucoup moins de mémoire, la mémoire utilisée est stockée dans la mémoire PermGen, pas Heap, et il est difficile d’expliquer aux clients comment augmenter l’allocation de ce type de mémoire.

Existe-t-il une alternative à intern () pour réduire la consommation de mémoire, (les avantages de == versus égal à la performance ne sont pas un problème pour moi)

Regardons les choses en face: le principal scénario d’utilisation est celui où vous lisez un stream de données (via un stream d’entrée ou un ResultSet JDBC) et où il y a une myriade de petites chaînes répétées tout au long.

Voici un petit truc qui vous donne un certain contrôle sur le type de mécanisme que vous souhaitez utiliser pour internaliser des chaînes de caractères et autres immuables, ainsi qu’un exemple d’implémentation:

 /** * Extends the notion of Ssortingng.intern() to different mechanisms and * different types. For example, an implementation can use an * LRUCache, or a WeakHashMap. */ public interface Internalizer { public T get(T obj); } public static class LRUInternalizer implements Internalizer { private final LRUCache cache; public LRUInternalizer(int size) { cache = new LRUCache(size) { private static final long serialVersionUID = 1L; @Override protected T resortingeve(T key) { return key; } }; } @Override public T get(T obj) { return cache.get(obj); } } public class PermGenInternalizer implements Internalizer { @Override public Ssortingng get(Ssortingng obj) { return obj.intern(); } } 

Je l’utilise souvent lorsque je lis des champs de stream ou de ResultSets. Remarque: LRUCache est un simple cache basé sur LinkedHashMap . Il appelle automatiquement la méthode resortingeve() fournie par l’utilisateur pour tous les échecs de cache.

La manière de l’utiliser est de créer un LRUInternalizer avant votre lecture (ou vos lectures), de l’utiliser pour internaliser des chaînes et d’autres petits objects immuables, puis de le libérer. Par exemple:

 Internalizer internalizer = new LRUInternalizer(2048); // ... get some object "input" that stream fields for (Ssortingng s : input.nextField()) { s = internalizer.get(s); // store s... } 

Je l’utilise pour mettre en cache le contenu d’environ 36000 codes liés à des noms associés. J’internes les chaînes dans le cache car beaucoup de codes pointent vers la même chaîne.

En internant les chaînes dans mon cache, je m’assure que les codes qui pointent vers la même chaîne pointent vers la même mémoire, ce qui me permet d’économiser de la mémoire vive.

Si les chaînes internes étaient en fait récupérées, cela ne fonctionnerait pas du tout pour moi. Cela reviendrait à nier l’objective de l’internement. Le mien ne sera pas récupéré car je détiens une référence à chaque chaîne du cache.

Le coût de l’internement d’une chaîne est beaucoup plus élevé que le temps économisé dans une seule comparaison avec ssortingngA.equals (B). Utilisez-le uniquement (pour des raisons de performances) lorsque vous utilisez à plusieurs resockets les mêmes variables de chaîne inchangées. Par exemple, si vous effectuez une itération régulière sur une liste stable de chaînes pour mettre à jour certaines cartes indexées sur le même champ de chaîne, vous pouvez obtenir une économie appréciable.

Je suggère d’utiliser l’internement de chaîne pour modifier les performances lorsque vous optimisez des parties spécifiques de votre code.

Rappelez-vous également que Ssortingng est immuable et ne fait pas l’erreur de

 Ssortingng a = SOME_RANDOM_VALUE a.intern() 

rappelez-vous de faire

 Ssortingng a = SOME_RANDOM_VALUE.intern() 

Si vous cherchez un remplacement illimité pour Ssortingng.intern, également des tâches inutiles, ce qui suit fonctionne bien pour moi.

 private static WeakHashMap> internSsortingngs = new WeakHashMap<>(); public static String internalize(String k) { synchronized (internStrings) { WeakReference weakReference = internSsortingngs.get(k); Ssortingng v = weakReference != null ? weakReference.get() : null; if (v == null) { v = k; internSsortingngs.put(v, new WeakReference(v)); } return v; } } 

Bien sûr, si vous pouvez estimer approximativement combien de chaînes différentes il y aura, utilisez simplement Ssortingng.intern () avec -XX: SsortingngTableSize = highEnoughValue .