Java Constant Pool est un sujet durable et est également le favori de l'intervieweur. Il existe de nombreux types de questions. Je vais le résumer cette fois.
théorie
Tout d'abord, exprimons la distribution de mémoire virtuelle JVM:
Le compteur de programmes est un pipeline pour JVM pour exécuter le programme, stockant certaines instructions de saut. C'est trop profond et je ne comprends pas.
La pile de méthode locale est la pile utilisée par JVM pour appeler les méthodes du système d'exploitation.
La pile de machines virtuelles est la pile utilisée par JVM pour exécuter le code Java.
Le domaine de la méthode stocke certaines constantes, variables statiques, informations de classe, etc., qui peuvent être comprises comme l'emplacement de stockage du fichier de classe en mémoire.
Le tas de machine virtuelle est le tas utilisé par JVM pour exécuter le code Java.
Les piscines constantes en Java sont en fait divisées en deux formes: les pools constants statiques et les piscines constantes d'exécution .
Le pool constant soi-disant statique est le pool constant du fichier * .class. Le pool constant du fichier de classe contient non seulement des littéraux de chaîne (numéro), mais contient également des informations sur les classes et les méthodes, occupant la majeure partie de l'espace du fichier de classe.
Le pool constant d'exécution est la machine virtuelle JVM charge le pool constant dans le fichier de classe en mémoire après avoir terminé l'opération de chargement de classe et l'enregistre dans la zone de la méthode . Le pool constant que nous appelons souvent fait référence au pool constant d'exécution dans la zone de la méthode.
Ensuite, nous citons quelques exemples de pools constants qui sont populaires sur Internet, puis les expliquons.
String S1 = "Hello"; String s2 = "Hello"; String s3 = "hel" + "lo"; String S4 = "Hel" + Nouvelle String ("LO"); String s5 = new String ("Hello"); String S6 = S5.Intern (); String S7 = "H"; String S8 = "Ello"; String S9 = S7 + S8; System.out.println (S1 == S2); // trueSystem.out.println (S1 == S3); // trueSystem.out.println (S1 == S4); // falSesystem.out.println (S1 == S9); // falsesystem.out.println (s4 == s5); // falsesystem.out.println (s1 == S6); // vraiTout d'abord, en Java, l'opérateur == est utilisé directement et les adresses de référence de deux chaînes sont comparées, et non le contenu. Veuillez utiliser String.equals () pour comparer le contenu.
S1 == S2 est très facile à comprendre. Lorsque S1 et S2 sont attribués, ils utilisent des littéraux de chaîne. Pour le dire franchement, ils écrivent directement la chaîne à mort. Pendant la compilation, ce littéral sera placé directement dans le pool constant du fichier de classe, réalisant ainsi la réutilisation. Après avoir chargé le pool constant au moment de l'exécution, S1 et S2 pointent vers la même adresse mémoire, ils sont donc égaux.
Il y a une fosse dans S1 == S3. Bien que S3 soit une chaîne épissée dynamiquement, toutes les parties impliquées dans l'épissage sont des littéraux connus. Pendant la période de compilation, cet épissage sera optimisé et le compilateur vous aidera directement à l'épliances. Par conséquent, String S3 = "HEL" + "LO"; est optimisé pour string s3 = "Bonjour"; Dans le fichier de classe, donc S1 == S3 est vrai.
S1 == S4 n'est bien sûr pas égal. Bien que S4 soit également épissé, la nouvelle partie de chaîne ("LO") n'est pas un littéral connu, mais une partie imprévisible. Le compilateur ne l'optimisera pas. Vous devez attendre la course pour déterminer le résultat. Combiné avec le théorème d'invariance des chaînes , vous savez où S4 est alloué, donc l'adresse doit être différente. Une brève image pour clarifier l'idée:
S1 == S9 n'est pas égal, et la raison est similaire. Bien que les littéraux de chaîne utilisés par S7 et S8 lors de l'attribution de valeurs, lors de l'épissage dans S9, S7 et S8, sont tous deux imprévisibles. Après tout, le compilateur est un compilateur et ne peut pas être utilisé comme interprète, il n'est donc pas optimisé. Lorsqu'il est exécuté, la nouvelle chaîne épissée en S7 et S8 n'est pas sûre dans le tas et ne peut pas être la même que l'adresse S1 dans le pool constant de la zone de méthode.
S4 == S5 n'est plus nécessaire pour être expliqué, il n'est certainement pas égal, les deux sont dans le tas, mais les adresses sont différentes.
L'égalité de S1 == S6 est complètement attribuée à la méthode des internes. S5 est dans le tas et le contenu est bonjour. La méthode interne essaiera d'ajouter la chaîne Hello au pool constant et de retourner son adresse dans le pool constant. Parce qu'il y a une chaîne HELLO dans le pool constant, la méthode interne renvoie directement l'adresse; Alors que S1 pointe déjà le pool constant pendant la période de compilation, S1 et S6 pointent de la même adresse, ce qui est égal.
À ce stade, nous pouvons tirer trois conclusions très importantes:
Vous devez prêter attention au comportement pendant la période de compilation afin de mieux comprendre le pool constant.
Les constantes dans le pool constant d'exécution proviennent essentiellement du pool constant de chaque fichier de classe.
Lorsque le programme est en cours d'exécution, JVM n'ajoutera pas automatiquement les constantes au pool constant à moins qu'il n'ajoute manuellement les constantes au pool constant (comme appeler la méthode des stagiaires).
Ce qui précède implique uniquement des pools constants de chaînes. En fait, il y a des pools constants entiers, des pools constants à points flottants, etc., mais ils sont similaires, mais des pools constants de types numériques ne peuvent pas être ajoutés manuellement. Les constantes dans le pool constant sont déterminées au début du programme. Par exemple, la plage constante dans le pool constant entier est: -128 ~ 127. Seuls les nombres dans cette plage peuvent être utilisés pour le pool constant.
pratique
Après avoir dit tellement de théorie, touchons la véritable piscine constante.
Comme mentionné précédemment, il existe un pool constant statique dans le fichier de classe. Ce pool constant est généré par le compilateur et est utilisé pour stocker les littéraux dans le fichier source Java (cet article se concentre uniquement sur les littéraux). Supposons que nous ayons le code Java suivant:
String s = "hi";
Pour plus de commodité, c'est aussi simple que c'est vrai! Après avoir compilé le code dans un fichier de classe, utilisez Winhex pour ouvrir le fichier de classe de format binaire. Comme indiqué dans l'image:
Expliquons brièvement la structure du fichier de classe. Les 4 octets au début sont le numéro magique du fichier de classe, qui est utilisé pour l'identifier comme fichier de classe. Pour le dire franchement, c'est l'en-tête de fichier, qui est: CA fe ba be.
Les 4 octets suivants sont le numéro de version de Java, et le numéro de version ici est 34, car l'auteur est compilé avec JDK8, et le numéro de version correspond au niveau de la version JDK. La version supérieure peut être compatible avec la version inférieure, mais la version inférieure ne peut pas exécuter la version supérieure. Donc, si un jour les lecteurs veulent savoir avec quelle version JDK le fichier de classe des autres personnes est compilé, vous pouvez consulter ces 4 octets.
Ensuite, l'entrée de la piscine constante. Le nombre de constantes de pool constantes est identifiée par 2 octets à l'entrée. Dans cet exemple, la valeur est 00 1A. Il est traduit en décimal et est de 26 ans, ce qui signifie qu'il y a 25 constantes. La 0e constante est une valeur spéciale, il n'y a donc que 25 constantes.
La piscine constante stocke divers types de constantes. Ils ont tous leurs propres types et leurs propres spécifications de stockage. Cet article se concentre uniquement sur les constantes de cordes. Les constantes de chaîne commencent avec 01 (1 octet), puis enregistrez la longueur de la chaîne avec 2 octets, puis le contenu réel de la chaîne. Dans ce cas, c'est: 01 00 02 68 69.
Ensuite, parlons de la piscine constante d'exécution. Étant donné que le pool constant d'exécution est dans la zone de la méthode, nous pouvons définir la taille de la zone de méthode via les paramètres JVM: -xx: permsize, -xx: maxpermSize, limitant ainsi indirectement la taille constante du pool.
Supposons que le paramètre de démarrage JVM soit: -xx: permSize = 2m -xx: maxpermSize = 2m, puis exécutez le code suivant:
// Gardez les références pour empêcher la liste de collecte automatique des ordures <string> list = new ArrayList <string> (); int i = 0; while (true) {// ajouter manuellement la liste constante.add (string.valueof (i ++). Intern ());};Le programme lancera immédiatement: exception dans le fil "Main" java.lang.outofMemoryError: exception de l'espace permgen. L'espace Permgen est la zone de méthode, ce qui est suffisant pour indiquer que le pool constant est dans la zone de la méthode.
Dans JDK8, la zone de méthode a été supprimée et la zone Metaspace a été remplacée. Par conséquent, nous devons utiliser le nouveau paramètre JVM: -xx: maxMetAspaceSize = 2m, et toujours exécuter le code ci-dessus, Throwing: java.lang.outofMemoryError: exception Metaspace. De même, il est expliqué que le pool constant d'exécution est divisé en zone Metaspace. Pour des connaissances spécifiques sur la zone Metaspace, veuillez la rechercher vous-même.
Tous les codes de cet article ont été testés et passés sous JDK7 et JDK8. D'autres versions de JDK peuvent avoir de légères différences. Veuillez l'explorer vous-même.