Le Javadoc sur String.intern()
ne donne pas beaucoup de détails. (En résumé: il renvoie une représentation canonique de la chaîne, permettant de comparer les chaînes internées à l'aide ==
)
- Quand devrais-je utiliser cette fonction en faveur de
String.equals()
? - Y a-t-il des effets secondaires non mentionnés dans le Javadoc, c'est-à-dire plus ou moins d'optimisation par le compilateur JIT?
- Y a-t-il d'autres utilisations
String.intern()
?
Réponses:
lorsque vous avez besoin de vitesse, car vous pouvez comparer les chaînes par référence (== est plus rapide qu'égal)
Le principal inconvénient est que vous devez vous rappeler de vous assurer que vous effectuez bien intern () toutes les chaînes que vous allez comparer. Il est facile d'oublier d'interner () toutes les chaînes et vous pouvez alors obtenir des résultats confus et incorrects. Aussi, pour le bien de tous, assurez-vous de documenter très clairement que vous comptez sur les chaînes en cours d'internalisation.
Le deuxième inconvénient si vous décidez d'internaliser des chaînes est que la méthode intern () est relativement coûteuse. Il doit gérer le pool de chaînes uniques afin de faire un bon travail (même si la chaîne a déjà été internalisée). Donc, soyez prudent dans la conception de votre code pour que vous, par exemple, intern () toutes les chaînes appropriées en entrée afin de ne plus avoir à 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 rencontrer une OutOfMemoryError avec beaucoup d'espace libre.
(de Michael Borgwardt)
la source
if (s1.equals(s2))
etif (i1 == i2)
est minime, sauf si vous avez beaucoup de longues chaînes avec les mêmes caractères principaux. Dans la plupart des utilisations réelles (autres que les URL), les chaînes diffèrent au sein des premiers caractères. Et les longues chaînes if-else sont de toute façon une odeur de code: utilisez des énumérations et des cartes de foncteurs.Cela n'a (presque) rien à voir avec la comparaison de chaînes. L'internement de chaînes est destiné à économiser de la mémoire si vous avez plusieurs chaînes avec le même contenu dans votre application. En utilisant
String.intern()
l'application, il n'y 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 vraiment facile de rompre en oubliant de n'interne que une seule instance).la source
str.intern()
quandstr
est"Hello"
.String.intern()
est définitivement récupéré dans les JVM modernes.La mémoire suivante ne manque JAMAIS, en raison de l'activité du GC:
Voir plus (de moi) sur le mythe de non GCed String.intern () .
la source
OutOfMemoryException
- non, pas le code ci-dessus, dans mon cerveau : lien vers l'article javaturning, qui pointe vers cet article, qui pointe vers l'article javaturning, qui ... :-)J'ai récemment écrit un article sur l'implémentation de String.intern () dans Java 6, 7 et 8: String.intern dans Java 6, 7 et 8 - regroupement de chaînes .
J'espère qu'il devrait contenir suffisamment d'informations sur la situation actuelle du pool de chaînes en Java.
En un mot:
String.intern()
en Java 6, car il va dans PermGenString.intern()
dans Java 7 et Java 8: il utilise 4 à 5 fois moins de mémoire que le roulement de votre propre pool d'objets-XX:StringTableSize
(la valeur par défaut est probablement trop petite; définissez un nombre premier)la source
La comparaison de chaînes avec == est beaucoup plus rapide qu'avec equals ()
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 petit que cela et le gain final sera dilué à quelques pour cent.
String.intern () éloigne la chaîne du tas et la place dans PermGen
Les chaînes internalisées sont placées dans une zone de stockage différente: la génération permanente qui est une zone de la JVM réservée aux objets non utilisateur, comme les classes, les méthodes et d'autres objets JVM internes. La taille de cette zone est limitée et est beaucoup plus précieuse que le tas. Cette zone étant plus petite que Heap, il y a plus de chances d'utiliser tout l'espace et d'obtenir une OutOfMemoryException.
Les chaînes String.intern () sont récupérées
Dans les nouvelles versions de JVM, les chaînes internalisées sont également récupérées lorsqu'elles ne sont référencées par aucun objet.
En gardant à l'esprit les 3 points ci-dessus, vous pouvez déduire que String intern () ne peut être utile que dans quelques situations lorsque vous faites beaucoup de comparaison de chaînes, mais il est préférable de ne pas utiliser de chaîne interne si vous ne savez pas exactement ce que vous faites ...
la source
Étant donné qu'ils font des choses différentes, probablement jamais.
L'internement de chaînes pour des raisons de performances afin que vous puissiez les comparer pour l'égalité des références ne sera utile que si vous maintenez les références aux chaînes pendant un certain temps - les chaînes provenant de l'entrée utilisateur ou d'E / S ne seront pas internées.
Cela signifie que dans votre application, vous recevez des entrées d'une source externe et les traitez en un objet qui a une valeur sémantique - un identificateur, par exemple - mais cet objet a un type qui ne se distingue pas des données brutes et a des règles différentes sur la façon dont le programmeur doit utilise le.
Il est presque toujours préférable de créer un
UserId
type qui est interné (il est facile de créer un mécanisme d'internement générique thread-safe) et agit comme une énumération ouverte, que de surcharger lejava.lang.String
type avec une sémantique de référence s'il se trouve qu'il s'agit d'un ID utilisateur.De cette façon, vous n'obtenez pas de confusion entre le fait qu'une chaîne particulière a été internée ou non et vous pouvez encapsuler tout comportement supplémentaire dont vous avez besoin dans l'énumération ouverte.
la source
Je ne connais aucun avantage, et s'il y en avait un, je pense que equals () utiliserait lui-même intern () en interne (ce qui n'est pas le cas).
Briser les mythes internes ()
la source
intern
et de très bonnes raisons quiequals
ne le font pas par défaut. Le lien que vous avez publié est complet. Le dernier paragraphe admet même qu'ilintern
a un scénario d'utilisation valide: un traitement de texte lourd (par exemple un analyseur). Conclure que «[XYZ] est dangereux si vous ne savez pas ce que vous faites» est si banal que ça fait mal physiquement.Daniel Brückner a absolument raison. L'internement de chaînes est destiné à économiser de la mémoire (tas). Notre système possède actuellement une table de hachage géante pour conserver certaines données. À mesure que le système évolue, la table de hachage sera suffisamment grande pour libérer de la mémoire (comme nous l'avons testé). En internant toutes les chaînes dupliquées tous les objets dans la table de hachage, il nous permet d'économiser une quantité importante d'espace de tas.
Également en Java 7, les chaînes internées ne vivent plus dans PermGen mais plutôt en tas. Vous n'avez donc pas à vous soucier de sa taille et oui, il est récupéré:
la source
String
instances. En regardant leur contenu, j'ai vu de nombreux doublons et j'ai décidé de passer àintern()
, ce qui a permis d'économiser des centaines de Mo.Je ne connais pas le niveau JIT, mais il y a un support direct de bytecode pour le pool de chaînes , qui est implémenté comme par magie et efficacement avec une
CONSTANT_String_info
structure dédiée (contrairement à la plupart des autres objets qui ont des représentations plus génériques).JVMS
JVMS 7 5.1 dit :
Bytecode
Il est également instructif de regarder l'implémentation du bytecode sur OpenJDK 7.
Si nous décompilons:
nous avons sur le bassin constant:
et
main
:Notez comment:
0
et3
: la mêmeldc #2
constante est chargée (les littéraux)12
: une nouvelle instance de chaîne est créée (avec#2
comme argument)35
:a
etc
sont comparés comme des objets normaux avecif_acmpne
La représentation des chaînes constantes est assez magique sur le bytecode:
new String
)et la citation JVMS ci-dessus semble dire que chaque fois que l'Utf8 pointé est le même, des instances identiques sont chargées par
ldc
.J'ai fait des tests similaires pour les champs et:
static final String s = "abc"
pointe vers la table des constantes via l' attribut ConstantValueldc
Bonus : comparez cela au pool Integer , qui n'a pas de prise en charge directe du bytecode (c'est-à-dire pas d'
CONSTANT_String_info
analogue).la source
J'examinerais la comparaison interne et == - au lieu de l'égalité uniquement dans le cas où la comparaison égale est un goulot d'étranglement dans plusieurs comparaisons de chaînes. 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é les chaînes de manière agressive, vous constaterez que les appels à intern () deviennent de plus en plus lents.
la source
Une sorte de fuite de mémoire peut provenir de l'utilisation de
subString()
lorsque le résultat est petit par rapport à la chaîne source et que l'objet a une longue durée de vie.La solution normale consiste à utiliser
new String( s.subString(...))
mais lorsque vous avez une classe qui stocke le résultat d'un potentiel / probablesubString(...)
et que vous n'avez aucun contrôle sur l'appelant, vous pouvez envisager de stocker lesintern()
arguments String passés au constructeur. Cela libère le grand tampon potentiel.la source
L'internement de chaînes est utile dans le cas où la
equals()
méthode est souvent invoquée car laequals()
méthode vérifie rapidement si les objets sont les mêmes au début de la méthode.Cela se produit généralement lors de la recherche dans un
Collection
code bien qu'un autre code puisse également effectuer des vérifications d'égalité de chaîne.Cependant, il y a un coût lié à l'internement.J'ai effectué une analyse comparative d'un code et j'ai constaté que le processus d'internement augmentait le temps d'exécution d'un facteur 10.
Le meilleur endroit pour effectuer 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 produit normalement aux étapes d'initialisation de votre application afin d'éviter la pénalité du premier utilisateur.
Un autre endroit où cela peut être fait est lors du traitement des entrées utilisateur qui pourraient être utilisées pour effectuer des recherches clés. Cela se produit normalement dans votre processeur de requêtes, notez que les chaînes internes doivent être transmises.
En dehors de cela, il est inutile de faire un internat dans le reste du code car cela ne donnera généralement aucun avantage.
la source
Je voterais pour que cela ne vaille pas la peine d'être entretenu.
La plupart du temps, il n'y aura aucun besoin et aucun avantage en termes de performances, à moins que votre code ne travaille beaucoup avec des sous-chaînes. Dans ce cas, la classe String 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, je soupçonne que cela entraînera simplement l'explosion de vos besoins en mémoire.
la source
http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html
affirme que
String.equals()
utilise"=="
pour comparer desString
objets avant, selonhttp://www.codeinstructions.com/2009/01/busting-javalangstringintern-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 sont susceptibles d'être toutes de la même longueur - BIC0417 est un casque de sécurité de cycliste, TIG0003 est un tigre mâle adulte vivant - vous avez probablement besoin de toutes sortes de licences pour en commander un. Et il vaut peut-être mieux commander un casque de sécurité en même temps.)
Il semble donc que vous ayez un avantage à remplacer vos cordes par leur
intern()
version, mais vous obtenez la sécurité - et la lisibilité et la conformité standard - -sans- en utilisant "==" pourequals()
dans votre programmation. Et la plupart de ce que je vais dire dépend de ce qui est vrai, si c'est vrai.Mais
String.equals()
teste-t-on que vous lui avez passé une chaîne et non un autre objet, avant de l'utiliser"=="
? Je ne suis pas qualifié pour le dire, mais je suppose que non, car la plupart desequals()
opérations de ce type seront de chaîne en chaîne, de sorte que le test est presque toujours réussi. En effet, prioriser "==" à l'intérieurString.equals()
implique une confiance que vous comparez fréquemment la chaîne au même objet réel.J'espère que personne n'est surpris que les lignes suivantes produisent un résultat "faux":
Mais si vous passez
i
ài.toString()
la deuxième ligne, c'est sûrtrue
.Les lieux où vous pourriez espérer bénéficier d'un stage incluent
Set
etMap
, évidemment. J'espère que les chaînes internes ont leurs codes de hachage mis en cache ... Je pense que ce serait une exigence. Et j'espère que je n'ai pas simplement donné 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 voulez que la mémoire utilisée par votre code de programme soit très petite. Si votre volume de chaînes -distinct- est très important, il peut être temps d'envisager d'utiliser un code de programme de base de données dédié pour les gérer, et un serveur de base de données distinct. De même, si vous pouvez améliorer un petit programme (qui doit s'exécuter dans 10000 instances simultanément) en ne lui stockant pas du tout ses chaînes.
Il semble inutile de créer une nouvelle chaîne, puis de la jeter immédiatement pour son
intern()
substitut, mais il n'y a pas d'alternative claire, sauf pour conserver la chaîne en double. Donc, vraiment, le coût d'exécution consiste à rechercher votre chaîne dans le pool interne puis à permettre au garbage collector de se débarrasser de l'original. Et si c'est un littéral de chaîne, il est déjà interné de toute façon.Je me demande si
intern()
un code de programme malveillant peut abuser pour détecter si certaines chaînes et leurs références d'objet existent déjà dans leintern()
pool, et donc 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 fiable, je suppose. Pourtant, c'est quelque chose à considérer sur les bibliothèques tierces que vous incluez dans votre programme pour stocker et mémoriser vos numéros PIN ATM!la source
La vraie raison d'utiliser stagiaire n'est pas la ci-dessus. Vous pouvez l'utiliser après une erreur de mémoire insuffisante. Beaucoup de chaînes dans un programme typique sont String.substring () d'une autre grande chaîne [pensez à retirer un nom d'utilisateur d'un fichier xml 100K. L'implémentation Java est la suivante: la sous-chaîne contient une référence à la chaîne d'origine et le début + la fin dans cette énorme chaîne. (La pensée derrière c'est une réutilisation de la même grosse chaîne)
Après 1000 gros fichiers, à partir desquels vous n'enregistrez que 1000 noms courts, vous garderez en mémoire les 1000 fichiers entiers! Solution: dans ce scénario, utilisez simplement smallsubstring.intern ()
la source
J'utilise stagiaire pour économiser de la mémoire, je conserve une grande quantité de données String en mémoire et passer à utiliser stagiaire () a économisé une énorme quantité de mémoire. Malheureusement, bien qu'il utilise beaucoup moins de mémoire, la mémoire qu'il utilise est stockée dans la mémoire PermGen et non 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 performance == versus equals ne sont pas un problème pour moi)
la source
Avouons-le: le scénario de cas d'utilisation principal est lorsque vous lisez un flux de données (soit via un flux d'entrée, soit depuis un jeu de résultats JDBC) et qu'il existe une myriade de petites chaînes qui sont répétées tout au long.
Voici une petite astuce qui vous donne un certain contrôle sur le type de mécanisme que vous souhaitez utiliser pour internaliser les chaînes et autres immutables, et un exemple d'implémentation:
J'utilise cela souvent lorsque je lis des champs à partir de flux ou de ResultSets. Remarque:
LRUCache
est un cache simple basé surLinkedHashMap<K,V>
. Il appelle automatiquement laretrieve()
méthode fournie par l'utilisateur pour tous les échecs de cache.La façon de l'utiliser consiste à en créer un
LRUInternalizer
avant votre lecture (ou lectures), à l'utiliser pour internaliser les chaînes et autres petits objets immuables, puis à les libérer. Par exemple:la source
Je l'utilise afin de mettre en cache le contenu d'environ 36 000 codes liés à des noms associés. J'interne les chaînes dans le cache car de nombreux 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 réellement vers la même mémoire, me permettant ainsi d'économiser de l'espace RAM.
Si les chaînes internées étaient en fait ramassées, cela ne marcherait pas du tout pour moi. Cela annulerait fondamentalement l'objectif du stage. Le mien ne sera pas récupéré parce que je détiens une référence à chaque chaîne dans le cache.
la source
Le coût de l'internement d'une chaîne est bien plus long que le temps gagné dans une seule comparaison stringA.equals (B). Utilisez-le uniquement (pour des raisons de performances) lorsque vous utilisez à plusieurs reprises les mêmes variables de chaîne inchangées. Par exemple, si vous parcourez régulièrement une liste stable de chaînes pour mettre à jour certaines cartes saisies sur le même champ de chaîne, vous pouvez obtenir une belle économie.
Je suggérerais d'utiliser l'internement de chaînes pour modifier les performances lorsque vous optimisez des parties spécifiques de votre code.
Rappelez-vous également que les cordes sont immuables et ne font pas l'erreur idiote de
n'oubliez pas de faire
la source
Si vous recherchez un remplacement illimité de String.intern, également récupéré, les éléments suivants fonctionnent bien pour moi.
Bien sûr, si vous pouvez approximativement estimer le nombre de chaînes différentes, utilisez simplement String.intern () avec -XX: StringTableSize = highEnoughValue .
la source