Je connais le concept de pool de constantes et le pool de constantes String utilisé par les machines virtuelles Java pour gérer les littéraux de chaîne. Mais je ne sais pas quel type de mémoire est utilisé par la machine virtuelle Java pour stocker les littéraux constants String. La pile ou le tas? Puisque c'est un littéral qui n'est associé à aucune instance, je suppose qu'il sera stocké dans une pile. Mais si ce n'est pas le cas, le littéral doit être recueilli par l'exécution de GC (corrigez-moi si je me trompe). Comment est-il géré s'il est stocké dans la pile?
La réponse est techniquement non plus. Selon la Java, la zone de stockage des littéraux de chaîne se trouve dans le pool de constantes d’exécution . La zone de mémoire du pool de constantes d’exécution est allouée par classe. ou par interface, de sorte qu’elle n’est liée à aucune instance d’objet. Le pool de constantes d’exécution est un sous-ensemble de la zone de méthode qui "stocke par - des structures de classe telles que le pool de constantes d’exécution, les données de champ et de méthode, ainsi que le code des méthodes et des constructeurs, y compris les méthodes spéciales utilisées dans l’initialisation de classe et d’instance et l’initialisation du type d’interface ". The VM spec indique que, bien que la zone de méthode fasse partie du tas, elle ne dicte pas que la mémoire allouée dans la zone de méthode soit soumise à un garbage collection ou d'autres comportements qui seraient associés aux structures de données normales allouées au segment de mémoire.
Comme expliqué par cette réponse , l'emplacement exact du pool de chaînes de caractères n'est pas spécifié et peut varier d'une implémentation JVM à une autre.
Il est intéressant de noter que jusqu’à Java 7, le pool se trouvait dans l’espace permgen du segment de mémoire sur la machine virtuelle à point mort, mais il a été déplacé vers la partie principale du segment de mémoire depuis = Java 7 :
Zone : HotSpot
Synopsis : dans JDK 7, les chaînes internées ne sont plus allouées lors de la génération permanente du Java = tas, mais sont plutôt alloués dans la partie principale du Java tas (connu sous le nom de jeunes générations et de personnes âgées) ), ainsi que les autres objets créés par l’application. Cette modification entraînera une augmentation du nombre de données résidant dans le segment principal Java), ainsi qu’un nombre moins important de données dans la génération permanente, de sorte que des tailles de segment En raison de cette modification, la plupart des applications ne verront que des différences relativement faibles d'utilisation du segment de mémoire, mais les applications plus volumineuses qui chargent de nombreuses classes ou utilisent beaucoup la méthode String.intern () verront des différences plus significatives. RFE: 6962931
Et dans Java 8 Hotspot, la génération permanente a été complètement supprimée.
Les littéraux de chaîne ne sont pas stockés sur la pile.
Littéraux de chaîne (ou plus précisément, les objets String qui les représentent) sont ont été historiquement stockés dans un segment appelé le segment "permgen". (Permgen est l'abréviation de génération permanente.)
Dans des circonstances normales, les littéraux de chaîne et la plupart des éléments contenus dans le tas de permgen sont accessibles de manière "permanente" et ne sont pas récupérés. (Par exemple, les littéraux de chaîne sont toujours accessibles à partir des objets de code qui les utilisent.) Toutefois, vous pouvez configurer une machine virtuelle Java pour tenter de rechercher et de collecter des classes chargées dynamiquement qui ne sont plus nécessaires, ce qui peut entraîner une récupération des littéraux de chaîne. .
CLARIFICATION # 1 - Je ne dis pas que Permgen ne soit pas pris par GC. Cela se produit généralement lorsque la JVM décide d’exécuter un GC complet. Mon point est que les littéraux String seront accessibles tant que le code qui les utilise est accessible, et le code sera accessible tant que le chargeur de classe du code est accessible, et pour les chargeur de classes par défaut, cela signifie "pour toujours".
CLARIFICATION # 2 - En fait, Java 7 stocke les objets String intern'd dans le segment de mémoire normal. Cela comprend (je présume) un objet String représentant les littéraux de chaîne. ( Voir la réponse de @ assylias pour plus de détails.)
Regroupement de chaînes
Le regroupement de chaînes (parfois appelé aussi canonisation de chaînes) est un processus de remplacement de plusieurs objets chaîne de valeur égale mais d'identité différente par un seul objet chaîne partagé. Vous pouvez atteindre cet objectif en conservant votre propre carte (avec éventuellement des références soft ou faibles en fonction de vos besoins) et en utilisant des valeurs de carte en tant que valeurs canonisées. Ou vous pouvez utiliser la méthode String.intern () qui vous est fournie par JDK.
À des moments de Java 6, l'utilisation de String.intern () était interdite par de nombreuses normes en raison d'une possibilité élevée d'obtenir une exception OutOfMemoryException si la mise en pool devenait incontrôlable. L’implémentation du pooling de chaînes dans Oracle Java 7 a été considérablement modifiée. Vous pouvez rechercher des détails dans http: //bugs.Sun.com/view_bug.do? Bug_id = 6962931 et http: //bugs.Sun.com/view_bug.do ? bug_id = 6962930 .
String.intern () dans Java 6
A l'époque, toutes les chaînes internées étaient stockées dans PermGen - la partie de taille fixe du segment de mémoire principalement utilisée pour stocker les classes chargées et le pool de chaînes. Outre les chaînes explicitement internées, le pool de chaînes PermGen contenait également toutes les chaînes littérales précédemment utilisées dans votre programme (le mot important ici est utilisé - si une classe ou une méthode n'a jamais été chargée/appelée, les constantes définies dans celle-ci ne seront pas chargées).
Le plus gros problème avec un tel pool de chaînes dans Java 6 était son emplacement - le PermGen. PermGen a une taille fixe et ne peut pas être étendu à l'exécution. Vous pouvez le définir en utilisant l'option -XX: MaxPermSize = 96m. Autant que je sache, la taille par défaut de PermGen varie entre 32 et 96 millions selon la plate-forme. Vous pouvez augmenter sa taille, mais sa taille sera toujours fixée. Une telle limitation nécessitait une utilisation très prudente de String.intern - vous feriez mieux de ne pas interner une entrée utilisateur non contrôlée utilisant cette méthode. C’est pourquoi le regroupement des chaînes à l’époque de Java 6 a été principalement mis en œuvre dans les cartes gérées manuellement.
String.intern () dans Java 7
Les ingénieurs Oracle ont apporté une modification extrêmement importante à la logique de regroupement de chaînes dans Java 7 - le pool de chaînes a été déplacé vers le segment de mémoire. Cela signifie que vous n'êtes plus limité par une zone mémoire séparée de taille fixe. Toutes les chaînes sont maintenant situées dans le tas, comme la plupart des objets ordinaires, ce qui vous permet de gérer uniquement la taille du tas tout en optimisant votre application. Techniquement, cela seul pourrait constituer une raison suffisante pour reconsidérer l'utilisation de String.intern () dans vos programmes Java 7. Mais il y a d'autres raisons.
Les valeurs du pool de chaînes sont récupérées
Oui, toutes les chaînes du pool de chaînes de la machine virtuelle Java sont éligibles pour la récupération de place si elles ne sont pas référencées à partir des racines de votre programme. Cela s'applique à toutes les versions discutées de Java. Cela signifie que si votre chaîne internée est hors de portée et qu'il n'y a pas d'autre référence, elle sera récupérée dans le pool de chaînes de la JVM.
Étant éligible pour la récupération de place et résidant dans le tas, un pool de chaînes JVM semble être un bon emplacement pour toutes vos chaînes, n'est-ce pas? En théorie, il est vrai que les chaînes non utilisées seront récupérées dans le pool. Les chaînes utilisées vous permettront d’économiser de la mémoire au cas où vous obtiendriez une chaîne égale de l’entrée. Semble être une stratégie parfaite de sauvegarde de la mémoire? Presque comme ça. Vous devez savoir comment le pool de chaînes est implémenté avant de prendre des décisions.
Comme d’autres réponses expliquent Memory in Java est divisé en deux parties
1. Pile: Une pile est créée par thread et stocke les cadres de pile qui stockent à nouveau les variables locales. Si une variable est un type de référence, cette variable fait référence à un emplacement mémoire dans le tas pour l'objet réel.
2. Tas: Tous les types d'objets seront créés dans le tas seulement.
La mémoire de tas est à nouveau divisée en 3 parties
1. Jeune génération: Stocke les objets qui ont une courte durée de vie. La jeune génération elle-même peut être divisée en deux catégories Eden Space et Espace du survivant .
2. Ancienne génération: Stocke les objets qui ont survécu à de nombreux cycles de nettoyage et qui sont toujours référencés.
3. Génération permanente: Stocke les métadonnées relatives au programme, par exemple. pool constant d'exécution.
Le pool de constantes de chaînes appartient à la zone de génération permanente de la mémoire de tas.
Nous pouvons voir le pool constant d'exécution pour notre code dans le bytecode en utilisant javap -verbose class_name
qui nous montrera les références de méthodes (#Methodref), les objets de classe (#Class), les littéraux de chaîne (#String)
Vous pouvez en savoir plus à ce sujet sur mon article Comment la JVM gère-t-elle la surcharge et la substitution de méthodes en interne .
Aux grandes réponses déjà incluses ici, je souhaite ajouter quelque chose qui manque dans mon point de vue - une illustration.
Comme vous avez déjà JVM, la mémoire allouée est divisée en un programme Java en deux parties. L’une est pile et l’autre est tas =. Stack est utilisé à des fins d'exécution et heap à des fins de stockage. Dans ce segment de mémoire, JVM alloue de la mémoire spécialement conçue pour les littéraux de chaîne. Cette partie de la mémoire est appelée pool de constantes de chaîne.
Ainsi, par exemple, si vous initiez les objets suivants:
String s1 = "abc";
String s2 = "123";
String obj1 = new String("abc");
String obj2 = new String("def");
String obj3 = new String("456);
Les littéraux de chaîne s1
Et s2
Iront dans le pool de constantes de chaînes, objets obj1, obj2, obj3 dans le tas. Tous seront référencés à partir de la pile.
De plus, veuillez noter que "abc" apparaîtra dans le tas et dans le pool constant de chaînes. Pourquoi String s1 = "abc"
Et String obj1 = new String("abc")
seront-ils créés de cette façon? C'est parce que String obj1 = new String("abc")
crée explicitement une nouvelle instance d'un objet String et distinct de manière référentielle et que String s1 = "abc"
Peut réutiliser une instance du pool de constantes de chaînes si celui-ci est disponible. . Pour une explication plus élaborée: https://stackoverflow.com/a/3298542/2811258