Qu'est-ce que le stage de chaîne Java?


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

5 answers

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

Essentiellement faire de la chaîne.intern () sur une série de chaînes veillera à ce 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 réellement alloué à la mémoire.

Cela peut être utile pour réduire les besoins en mémoire de votre programme. Mais sachez que le cache est maintenu par JVM en permanent pool de mémoire qui est généralement limité en taille par rapport au tas, vous ne devez donc pas utiliser intern si vous n'avez pas trop de valeurs en double.


Plus sur les contraintes de mémoire de l'utilisation de intern ()

D'une part, il est vrai que vous pouvez supprimer les doublons de chaîne en de les internaliser. Le problème est que les chaînes internalisées vont à la Génération permanente, qui est une zone de la JVM réservée pour les objets non-utilisateur, comme les Classes, les 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 intern () sur une chaîne a pour effet de déplacer sortir du tas dans la génération permanente, et vous risque à court 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 JDK 7, les chaînes internées ne sont plus allouées dans la génération permanente du tas Java, mais sont plutôt allouées dans la partie principale du tas Java (connu sous le nom de jeunes et anciennes générations), ainsi que les autres objets créés par l'application. Ce changement entraînera 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 de tas à ajuster. La plupart des applications ne verront que des différences relativement faibles dans l'utilisation du tas en raison de ce changement, mais des applications plus grandes qui chargez de nombreuses classes ou faites un usage intensif de la chaîne.la méthode intern() verra des différences plus significatives.

From De Java SE 7 Fonctionnalités et améliorations

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

 181
Author: Ashwinee K Jha, 2014-01-27 06:55:57

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

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

Si Vous devez comparer les chaînes, Vous devez utiliser equals(). Ce qui précède imprimera equals, car le testString est déjà interné pour Vous par le compilateur. Vous pouvez interner les chaînes vous-même en utilisant la méthode intern comme indiqué dans les réponses précédentes....

 53
Author: maslan, 2016-09-01 12:54:29

JLS

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

De plus, un littéral de chaîne fait toujours référence à la même instance de class String. En effet, les littéraux de chaîne-ou, plus généralement, les chaînes qui sont les valeurs des expressions constantes (§15.28) - sont "internés" de manière à partager des instances uniques, en utilisant la méthode String.stagiaire.

Exemple 3.10.5-1. 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

JVM

JVMS 7 5.1 dit dit que l'internement est implémenté magiquement et efficacement avec une structure dédiée CONSTANT_String_info (contrairement à la plupart des autres objets qui ont des représentations plus génériques):

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

Le langage de programmation Java exige que les littéraux de chaîne identiques (c'est-à-dire les littéraux contenant la même séquence de points de code) se réfèrent à la même instance de chaîne de classe (JLS §3.10.5). En outre, si la chaîne de méthode.stagiaire est appelé à n'importe quelle chaîne, le résultat est une référence à la même classe instance qui serait renvoyée si cette chaîne apparaissait en tant que 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_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 CONSTANT_String_info structure, alors le résultat de la dérivation littérale de chaîne est une référence à cette même instance de chaîne de classe.

  • Sinon, une nouvelle instance de chaîne de classe est créée contenant la séquence de points de code Unicode donnés 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 chaîne. Enfin, la méthode intern de la nouvelle instance de chaîne est appelée.

Bytecode

Décompilons certains bytecode OpenJDK 7 pour voir le stage en action.

Si on décompile:

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 le pool constant:

#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

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éé (avec #2 comme argument)
  • 35: a et {[16] } sont comparés en tant qu'objets réguliers avec if_acmpne

La représentation de La constante strings est assez 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 pointe vers une structure CONSTANT_Utf8_info qui contient les données. C'est les seules données nécessaires pour représenter la chaîne.

Et la citation JVM ci-dessus semble dire que chaque fois que l'Utf8 pointé est le même, alors des instances identiques sont chargées par ldc.

J'ai fait tests similaires pour les champs, et:

  • static final String s = "abc" pointe vers la table constante via l'attribut ConstantValue
  • les champs non finaux n'ont pas cet attribut, mais peuvent toujours être initialisés avec ldc

Conclusion: il existe un support direct du bytecode pour le pool de chaînes, et la représentation de la mémoire est efficace.

Bonus: comparez cela au Pool entier, qui n'a pas de prise en charge directe du bytecode (c'est-à-dire non CONSTANT_String_info analogique).

Mise à Jour pour Java 8 ou plus. Dans Java 8, l'espace PermGen (Génération permanente) est supprimé et remplacé par un espace Méta. La mémoire du pool de chaînes est déplacée vers le tas de JVM.

Par rapport à Java 7, la taille du pool de chaînes est augmentée dans le tas. Par conséquent, vous avez plus d'espace pour les chaînes internalisées, mais vous avez moins de mémoire pour toute l'application.

Encore une chose, vous savez déjà que lors de la comparaison de 2 (références de) objets en Java, '== ' est utilisé pour comparer la référence de l'objet, 'equals " est utilisé pour comparer le contenu de l'objet.

Vérifions 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 devriez utiliser 'equals' pour comparer 2 objets String. Et c'est ainsi que intern() est utile.

 2
Author: nguyentt, 2018-08-07 20:39:55

Chaîne de stage est une technique d'optimisation par le compilateur. Si vous avez deux littéraux de chaîne identiques dans une unité de compilation, le code généré garantit qu'il n'y a qu'un seul objet string créé pour toute l'instance de ce littéral(caractères entre guillemets doubles) dans l'assembly.

Je viens d'arrière-plan C#, donc je peux expliquer en donnant un exemple à partir de cela:

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

Sortie des comparaisons suivantes:

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

Note1:les Objets sont comparé par référence.

Note2: typeof(int).Name est évalué par la méthode de réflexion afin 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 car ils contiennent tous deux le même littéral et donc le code généré n'aura qu'un seul objet référençant "Int32". Voir la note 1 .

2) true car le contenu des deux valeurs est vérifié qui est le même.

3) FAUX car str2 et obj n'ont pas le même littéral. VoirNote 2 .

 0
Author: Robin Gupta, 2017-09-24 04:51:15