Qu'est-ce que Java String interning?

Ce qui est Chaîne de Stage en Java, quand je dois l'utiliser, et pourquoi?

171

5 réponses

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern ()

fait essentiellement de la ficelle.stagiaire() sur une série de cordes, de s'assurer que toutes les chaînes ayant le même contenu partagent la même mémoire. Donc, si vous avez une liste de noms où' john ' apparaît 1000 fois, en internant, vous vous assurez qu'un seul 'john' est effectivement attribué de la mémoire.

Cela peut être utile pour réduire les besoins en mémoire de votre programme. Mais sachant que le cache est maintenu par JVM dans un pool de mémoire permanent qui est généralement de taille limitée par rapport à heap, vous ne devriez donc pas utiliser intern si vous n'avez pas trop de valeurs dupliquées.


pour en savoir plus sur les contraintes de mémoire liées à l'utilisation de intern ()

d'un côté, il est vrai que vous pouvez supprimer les doublons de chaîne par de les internaliser. Le problème est que les cordes internalisées vont à la Génération Permanente, qui est un domaine de L'ECVM qui est réservé pour les objets non-utilisateurs, comme les Classes, méthodes et autres JVM internes objet. La taille de cette zone est limitée, et est généralement beaucoup plus petit que le tas. L'appel de stagiaire() sur une Chaîne a pour effet de déplacer c'est la génération permanente, et vous risquez manque D'Espace PermGen.

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


de JDK 7 (je veux dire dans HotSpot), quelque chose a changé.

Dans le JDK 7, interné les chaînes ne sont plus attribués dans la génération permanente du tas Java, mais sont à la place allouée à la partie principale de la Java heap (connu comme les jeunes et les anciennes générations), avec les autres objets créés par l' application. Ce changement se traduira par Plus de données résidant dans le tas Java principal, et moins de données dans la génération permanente, et peut donc nécessiter des tailles tas à ajuster. La plupart des applications verront seulement des différences relativement petites dans l'utilisation de tas en raison de ce changement, mais les applications plus grandes qui chargent de nombreuses classes ou font une utilisation lourde de la chaîne.la méthode intern() verra des différences plus significatives.

, De Java SE 7 Fonctions et Améliorations

mise à jour: les chaînes internes sont stockées dans le tas principal à partir de Java 7. http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

181
répondu Ashwinee K Jha 2014-01-27 06:55:57

Il y a quelques "accrocheur interview" questions pourquoi Vous obtenez

String s1 = "testString";
String s2 = "testString";
if(s1 == s2)System.out.println("equals!");

si vous comparez les chaînes, vous devez utiliser equals() . Le ci-dessus imprimera égal, parce que le testString est déjà interné pour vous par le compilateur. Vous pouvez interner les chaînes de caractères vous-même en utilisant la méthode intern comme indiqué dans les réponses précédentes....

53
répondu maslan 2016-09-01 12:54:29

JLS

JLS 7 3.10.5 le définit et donne un exemple pratique:

de plus, une chaîne littérale renvoie toujours à la même instance de la classe String. Cela est dû au fait que les littérales de chaîne - ou, plus généralement, les chaînes qui sont les valeurs des expressions constantes (§15.28) - sont "internées" de manière à partager des instances uniques, en utilisant la chaîne de méthode.stagiaire.

exemple 3.10.5-1. Les Littéraux De Chaîne

le programme composé de l'Unité de compilation (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

et l'Unité de compilation:

package other;
public class Other { public static String hello = "Hello"; }

produit la sortie:

true true true true false true

JVMS

JVM 7 5.1 dit dit que le stage est mis en œuvre comme par magie et efficacement avec une dédié CONSTANT_String_info struct (contrairement à la plupart des autres objets qui ont des représentations plus génériques):

une chaîne littérale est une référence à une instance de classe String, et est dérivée d'une structure CONSTANT_String_info (§4.4.3) dans la représentation binaire d'une classe ou d'une interface. La structure CONSTANT_String_info donne la séquence des points de code Unicode constituant la chaîne littérale.

le langage de programmation Java exige que les littérales à chaîne identiques (c'est-à-dire les littérales qui contiennent la même séquence de points de code) doivent se référer à la même instance de chaîne de classe (JLS §3.10.5). En outre, si la chaîne de méthode.intern est appelé sur n'importe quelle chaîne, le résultat est une référence à la même instance de classe qui serait retournée si cette chaîne apparaissait comme une littérale. Ainsi, l'expression suivante doit avoir la valeur true:

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

pour dériver une chaîne littérale, La Java Virtual Machine examine la séquence de points de code donnée par la structure CONSTANT_String_info.

  • si la chaîne de méthode.intern a déjà été appelé sur une instance de Class String contenant une séquence de points de code Unicode identique à celle donnée par la structure CONSTANT_String_info, alors le résultat de string literal derivation est une référence à cette même instance de class String.

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

Bytecode

décomposons un bytecode OpenJDK 7 pour voir interning en action.

si nous décompilons:

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

nous avons sur la piscine constante:

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

et main :

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)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/String;)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/String;)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

Note comment:

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

la représentation des chaînes constantes est tout à fait magique sur le bytecode:

  • il a une structure dédiée CONSTANT_String_info contrairement aux objets réguliers (par exemple new String )
  • la structure indique un CONSTANT_Utf8_info Structure que contient les données. C'est les seules données nécessaires pour représenter la chaîne.

et la citation JVMS ci-dessus semble dire que chaque fois que L'Utf8 pointé vers est le même, alors les instances identiques sont chargées par ldc .

j'ai fait des tests similaires pour les champs, et:

  • static final String s = "abc" points à la table constante à travers L'attribut ConstantValue
  • les champs non-finaux n'ont pas cet attribut, mais peuvent encore être initialisés avec ldc

Conclusion : il y a un support direct de bytecode pour le pool de cordes, et la représentation en mémoire est efficace.

Bonus: comparez cela au integer pool , qui n'a pas de support de bytecode direct (i.e. pas d'analogique CONSTANT_String_info ).

31

mise à jour pour Java 8 ou plus . En Java 8, PermGen (Permanent Generation) space est supprimé et remplacé par Meta Space. La mémoire du pool de cordes est déplacée dans le tas de JVM.

comparé à Java 7, La Taille du pool de chaînes est augmentée dans le tas. Par conséquent, vous avez plus d'espace pour intériorisé les Cordes, mais vous avez moins de mémoire pour l'ensemble de l'application.

encore Une chose, vous avez déjà connu que lorsque comparer 2 (referrences des) objets en Java ' == est utilisé pour comparer la référence de l'objet, ' equals est utilisé pour comparer le contenu de l'objet.

voyons ce code:

String value1 = "70";
String value2 = "70";
String value3 = new Integer(70).toString();

résultat:

value1 == value2 ---> vrai

value1 == value3 ---> faux

value1.equals(value3) ---> vrai

value1 == value3.intern() ---> vrai

C'est pourquoi vous devez utiliser ' equals ' pour comparer 2 objets String. Et c'est ainsi que intern() est utile.

2
répondu nguyentt 2018-08-07 20:39:55

string interning est une technique d'optimisation du compilateur. Si vous avez deux chaînes de caractères littérales identiques dans une unité de compilation, alors le code généré garantit qu'il n'y a qu'un seul objet de chaîne de caractères créé pour toute l'instance de ce littéral(caractères inclus dans des guillemets doubles) au sein de l'Assemblée.

je suis de C # background, donc je peux expliquer en donnant un exemple de cela:

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;

produit des comparaisons suivantes:

Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true    
Console.WriteLine(obj == str2); // false !?

Note1 : les objets sont comparés par référence.

Note2 : typeof(int).Le nom est évalué par méthode de réflexion de sorte qu'il ne soit pas évalué au moment de la compilation. Ici, ces comparaisons sont faites au moment de la compilation.

analyse des résultats: 1) vrai parce qu'ils contiennent tous les deux le même littéral et donc le code généré il n'y aura qu'un seul objet faisant référence à "Int32". Voir Note 1 .

2) vrai parce que le contenu des deux valeurs est vérifié qui est le même.

3) faux parce que str2 et obj n'ont pas le même littéral. Voir Note 2 .

0
répondu Robin Gupta 2017-09-24 04:51:15