Maison  >  Article  >  Java  >  Compréhension approfondie de l'allocation de mémoire Java (images et texte)

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

黄舟
黄舟original
2017-03-27 10:50:501559parcourir

Cet article présente principalement des informations pertinentes pour une compréhension approfondie de l'allocation de mémoire Java. Les amis qui en ont besoin peuvent s'y référer

Compréhension approfondie de l'allocation de mémoire Java

Cet article sera rédigé par Qian Cet article présente en profondeur les principes d'allocation de mémoire Java pour aider les novices à apprendre Java plus facilement. Il existe de nombreux articles de ce type en ligne, mais la plupart sont fragmentés. Cet article donnera aux lecteurs une introduction systématique du point de vue des processus cognitifs.

La première chose que vous devez savoir avant d'aborder le sujet est que les programmes Java s'exécutent sur JVM (Java Virtual Machine, Java Virtual Machine peut être compris comme un pont entre les programmes Java et les systèmes d'exploitation). implémente l'indépendance de la plate-forme Java montre l'importance de la JVM. Par conséquent, lors de l'apprentissage des principes d'allocation de mémoire Java, vous devez vous rappeler que tout se fait dans la JVM. La JVM est la base et la prémisse du principe d'allocation de mémoire.

En termes simples, un processus complet d'exécution d'un programme Java impliquera les zones de mémoire suivantes :

l Registre : Registre virtuel interne JVM, L'accès la vitesse est très rapide et le programme est incontrôlable.

l 🎜>, c'est-à-dire la référence (pointeur) de la zone du tas objet . Peut également être utilisé pour enregistrer des images lors du chargement des méthodes. l Heap : est utilisé pour stocker des données générées dynamiquement, telles que de nouveaux objets . Notez que les objets créés contiennent uniquement leurs variables membres respectives et n'incluent pas de méthodes membres. Étant donné que les objets de la même classe ont leurs propres variables membres et sont stockés dans leurs propres tas, mais qu'ils partagent les méthodes de la classe, les méthodes membres ne sont pas copiées à chaque fois qu'un objet est créé.

l Pool constant : JVM maintient un pool constant pour chaque type chargé. Le pool constant est une collection ordonnée de constantes utilisées par ce type. Y compris les constantes directes (types de base, String) et les références de symboles (1) à d'autres types, méthodes et champs. Les données du pool sont accessibles via un index, tout comme les tableaux. Étant donné que le pool de constantes contient toutes les références symboliques d'un type à d'autres types, méthodes et champs, le pool de constantes joue un rôle central dans la liaison dynamique de Java.

Le pool constant existe dans le tas

. l Segment de code : est utilisé pour stocker le code du programme source lu sur le disque dur. l

Segment de données :

est utilisé pour stocker les membres statiques définis par

statique

. Voici le diagramme de représentation de la mémoire :


L'image ci-dessus décrit grossièrement l'allocation de mémoire Java, puis explique en détail comment les programmes Java s'exécutent en mémoire à travers des exemples (Remarque : les images suivantes sont tirées du didacticiel J2SE du professeur Ma Bingbing de Shangxuetang. Le côté droit de l'image est le code du programme, et le côté gauche est le diagramme d’affectation, j’ajouterai des notes une par une).

Compréhension approfondie de l'allocation de mémoire Java (images et texte)Connaissances préliminaires :

1.

Un fichier Java, tant qu'il existe une méthode de saisie main, nous pense que c'est le cas. Un programme Java peut être compilé et exécuté indépendamment.

2.Qu'il s'agisse de variables de type ordinaire ou de variables de type référence (communément appelées instances), elles peuvent être utilisées comme variables locales, et elles peuvent apparaître sur la pile. C'est juste que les variables de type ordinaire stockent leurs valeurs correspondantes directement sur la pile, tandis que les variables de type référence stockent un pointeur vers la zone du tas. Grâce à ce pointeur, vous pouvez trouver l'objet correspondant à cette instance dans la zone du tas. Par conséquent, les variables de type ordinaire n'occupent qu'une partie de la mémoire dans la zone de pile, tandis que les variables de type référence occupent une partie de la mémoire dans la zone de pile et la zone de tas.

Exemple :


Compréhension approfondie de l'allocation de mémoire Java (images et texte)                                                                                                                                                                                                                                                          .

 2.Créez une variable de type int date Puisqu'il s'agit d'un type basique, la valeur 9 correspondant à la date est directement stockée dans la pile.

  3.Créez deux instances de la classe BirthDate, d1 et d2, et stockez les pointeurs correspondants pointant vers leurs objets respectifs dans la pile. Ils appellent le constructeur paramétré lors de l'instanciation, il y a donc des valeurs initiales personnalisées dans l'objet.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

    Appelez la méthode change1 de l'objet de test avec la date comme paramètre. Lorsque la JVM lit ce code, elle détecte que i est une variable locale, elle place donc i sur la pile et attribue la valeur de date à i.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

   Attribuez 1234 à i. Étape très simple.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

                                                                                                                                                             Une fois la méthode change1 exécutée, l'espace de pile occupé par la variable locale i est immédiatement libéré.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

                    Appelez la méthode change2 de l'objet de test, en prenant l'instance d1 comme paramètre. La JVM détecte que le paramètre b dans la méthode change2 est une variable locale et l'ajoute immédiatement à la pile. Puisqu'il s'agit d'une variable de type référence, b stocke le pointeur dans d1. À ce moment, b et d1 pointent vers des objets dans le. même tas. Ce qui est passé entre b et d1 est un pointeur.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

                                                                                                                                                                           

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

Le processus d'exécution interne est le suivant : créer un nouvel objet dans la zone du tas, et enregistrer le pointeur de l'objet dans l'espace correspondant à b dans la pile. A ce moment, l'instance b ne pointe plus vers l'objet pointé par l'instance d1, mais l'objet pointé par l'instance d1 Il n'y a aucun changement, il ne peut donc avoir aucun impact sur d1.

Compréhension approfondie de l'allocation de mémoire Java (images et texte)

                                                                    Si l'espace de la pile est perdu, le tas l'espace doit attendre le recyclage automatique.


Compréhension approfondie de l'allocation de mémoire Java (images et texte)                    

Appelez la méthode change3 de l'instance de test, en prenant l'instance d2 comme paramètre. De la même manière, la JVM allouera de l'espace pour la variable de référence locale b sur la pile et stockera le pointeur dans d2 dans b. À ce moment, d2 et b pointent vers le même objet. Ensuite, l’appel de la méthode setDay de l’instance b appelle en fait la méthode setDay de l’objet pointé par d2.


Compréhension approfondie de l'allocation de mémoire Java (images et texte)            

L'appel de la méthode setDay de l'instance b affectera d2, car les deux pointent vers le même un objet.


Une fois la méthode change3 exécutée, la variable de référence locale b est immédiatement libérée.

Ce qui précède est la situation générale de l'allocation de mémoire lorsqu'un programme Java est en cours d'exécution. En fait, ce n’est rien. C’est très simple une fois qu’on maîtrise l’idée. Il n'y a rien de plus que deux types de variables : les types de base et les types de référence. Les deux sont placés sur la pile en tant que variables locales. Le type de base enregistre la valeur directement sur la pile. Le type référence enregistre uniquement un pointeur vers la zone du tas, et l'objet réel est dans le tas. Lorsqu'il est utilisé comme paramètre, le type de base est passé directement par valeur et le type de référence est passé par le pointeur. Résumé :    

1.Effacer ce qu'est une instance et ce qu'est un objet. Class a= new Class();À l'heure actuelle, a est appelé une instance, et a ne peut pas être considéré comme un objet. L'instance est sur la pile et l'objet est sur le tas. L'exploitation de l'instance exploite en fait l'objet indirectement via le pointeur d'instance. Plusieurs instances peuvent pointer vers le même objet.

2.Les données dans la pile et la destruction des données dans le tas ne sont pas synchronisées. Une fois la méthode terminée, les variables locales de la pile sont immédiatement détruites, mais les objets du tas ne sont pas nécessairement détruits. Comme il peut y avoir d'autres variables pointant vers cet objet, il ne sera pas détruit tant qu'il n'y aura plus de variable dans la pile pointant vers l'objet dans le tas, et il ne sera pas détruit immédiatement avant l'analyse du garbage collection. .

 3.La pile, le tas, le segment de code, le segment de données, etc. ci-dessus sont tous relatifs au programme d'application. Chaque application correspond à une instance JVM unique. Chaque instance JVM possède sa propre zone mémoire et ne s'affecte pas les unes les autres. Et ces zones mémoire sont partagées par tous les threads. Les piles et tas mentionnés ici sont des concepts généraux, et ces piles peuvent également être subdivisées.

 4.Les variables membres d'une classe sont différentes selon les objets et ont leur propre espace de stockage (les variables membres sont dans les objets du tas). Cependant, les méthodes d'une classe sont partagées par tous les objets de la classe. Il n'y a qu'un seul ensemble. Les méthodes sont poussées sur la pile lorsque l'objet utilise la méthode. Si la méthode n'est pas utilisée, elle n'occupe pas de mémoire.

L'analyse ci-dessus ne concerne que la pile et le tas, et il existe également une zone mémoire très importante : le pool constant, où surviennent souvent des problèmes inexplicables. Le but du pool de constantes a été expliqué ci-dessus, et il n'est pas nécessaire de le comprendre en profondeur. N'oubliez pas qu'il conserve les constantes d'une classe chargée. Ensuite, nous illustrerons les caractéristiques du pool constant avec quelques exemples.

Connaissances préliminaires :

Types de base et classes wrapper des types de base. Les types de base sont : byte, short, char, int, long, boolean. Les types de base de classes d'empaquetage sont : Byte, Short, Character, Integer, Long et Boolean. Notez qu'il est sensible à la casse. La différence entre les deux est la suivante : le type de base est reflété dans le programme en tant que variable ordinaire et la classe wrapper du type de base est une classe qui est reflétée dans le programme en tant que variable de référence. Par conséquent, ils sont stockés à différents emplacements de la mémoire : les types primitifs sont stockés sur la pile, tandis que les classes wrapper primitives sont stockées sur le tas. Les classes wrapper mentionnées ci-dessus implémentent toutes la technologie de pool constant, mais pas les deux autres classes wrapper de type nombre à virgule flottante. De plus, le type String implémente également la technologie de pool constant.

Exemple :

public class test { 
  public static void main(String[] args) {   
    objPoolTest(); 
  } 
 
  public static void objPoolTest() { 
    int i = 40; 
    int i0 = 40; 
    Integer i1 = 40; 
    Integer i2 = 40; 
    Integer i3 = 0; 
    Integer i4 = new Integer(40); 
    Integer i5 = new Integer(40); 
    Integer i6 = new Integer(0); 
    Double d1=1.0; 
    Double d2=1.0; 
     
    System.out.println("i=i0\t" + (i == i0)); 
    System.out.println("i1=i2\t" + (i1 == i2)); 
    System.out.println("i1=i2+i3\t" + (i1 == i2 + i3)); 
    System.out.println("i4=i5\t" + (i4 == i5)); 
    System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));   
    System.out.println("d1=d2\t" + (d1==d2));  
     
    System.out.println();     
  } 
}

Résultat :

i=i0  true 
i1=i2  true 
i1=i2+i3    true 
i4=i5  false 
i4=i5+i6    true 
d1=d2  false

RésultatAnalyse

1.i et i0 sont tous deux des variables de type commun (int), donc les données sont stockées directement dans la pile, et la pile a une fonctionnalité très importante : Les données de la pile peuvent être partagées. Lorsque nous définissons int i = 40;, puis définissons int i0 = 40;, nous vérifierons automatiquement s'il y a 40 dans la pile. Si tel est le cas, i0 pointera directement vers i 40 et aucun nouveau 40 ne sera ajouté.

2.i1 et i2 sont tous deux des types de référence et stockent des pointeurs sur la pile car Integer est une classe wrapper. Étant donné que la classe de packaging Integer implémente la technologie de pool constant, les 40 de i1 et i2 sont tous deux obtenus à partir du pool constant et pointent vers la même adresse, donc i1=12.

3.Il s'agit évidemment d'une opération d'addition, Les opérations mathématiques de Java sont toutes effectuées sur la pile, Java calculera automatiquement i1 et i2 L'opération de déballage est effectué et converti en un entier , donc i1 est numériquement égal à i2+i3.

4.i4 et i5 sont tous deux des types de référence et stockent des pointeurs sur la pile car Integer est une classe wrapper. Cependant, comme ils sont chacun nouveaux, ils ne recherchent plus les données du pool constant, mais créent un objet du tas, puis chacun enregistre le pointeur vers l'objet, donc i4 et i5 ne sont pas égaux car ils stockent des pointeurs différents. Les objets pointus sont différents.

5.C'est aussi une opération d'addition, la même que 3.

6.d1 et d2 sont tous deux des types de référence et stockent des pointeurs sur la pile car Double est une classe wrapper. Cependant, la classe d'empaquetage Double n'implémente pas la technologie de pool constant, donc Doubled1=1.0; est équivalent à Double d1=new Double(1.0);, qui est un nouvel objet du tas, et il en va de même pour d2. Par conséquent, les pointeurs stockés dans d1 et d2 sont différents et pointent vers des objets différents, ils ne sont donc pas égaux.

Résumé :

1.Les types de base de classes d'emballage mentionnés ci-dessus mettent tous en œuvre la technologie de pool constant, mais les constantes qu'ils maintiennent ne sont que des constantes comprises dans la plage [-128 à 127]. , les objets seront créés à partir du tas et ne seront plus extraits du pool constant. Par exemple, si l'exemple ci-dessus est remplacé par Integer i1 = 400 ; Integer i2 = 400 ;, il est évident qu'il dépasse 127 et que la constante ne peut pas être obtenue à partir du pool de constantes, un nouvel objet Integer doit donc être créé à partir du tas. À l’heure actuelle, i1 et i2 ne sont pas égaux.

2. Le type String implémente également la technologie de pool constant, mais c'est légèrement différent. Le type String détecte d'abord s'il existe une chaîne correspondante dans le pool de constantes. S'il y en a, elle sera supprimée, sinon, la chaîne actuelle sera ajoutée.

Note de bas de page :

(1) La référence de symbole, comme son nom l'indique, est un symbole. La référence du symbole est Ce symbole sera analysé uniquement lorsqu'il est utilisé. Si vous êtes familier avec le système Linux ou Unix, vous pouvez considérer cette référence de symbole comme un lien symbolique vers un fichier. Lorsque vous utilisez ce lien symbolique, il sera en fait analysé et développé pour trouver le fichier réel.

Pour les références de symboles, il y a plus de discussions au niveau du chargement de la classe, et le niveau du code source n'est qu'une discussion formelle.

Lorsqu'une classe est chargée, les références symboliques des autres classes utilisées par la classe seront enregistrées dans le pool de constantes. Lorsque le code réel est exécuté, la JVM enregistrera le pool de constantes lorsqu'elle rencontrera une autre classe. la première fois. La référence symbolique de la classe est développée et convertie en référence directe, de sorte que la prochaine fois que le même type sera rencontré, la JVM ne l'analysera plus, mais utilisera directement cette référence directe qui a été analysée.

En plus de l'instruction de référence symbolique ci-dessus dans le processus de chargement de classe, au niveau du code source, elle est basée sur le processus d'analyse de référence pour distinguer si certaines données du code sont une référence symbolique ou une référence directe. , tel que System.out.println("test" + "abc");//L'effet qui se produit ici est équivalent à une référence directe, et en supposant un certain Strings = "abc" ; System.out. println("test" + s);//L'effet ici est équivalent à une référence symbolique, c'est-à-dire que s est développé et analysé, ce qui équivaut à s étant un lien symbolique de "abc", c'est-à-dire dans le fichier compilé. À ce stade, le fichier de classe ne développe pas directement les s, mais traite ce s comme un symbole, qui sera développé lors de l'exécution du code réel.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn