Maison >Java >javaDidacticiel >Introduction détaillée à la mémoire tas et à la mémoire pile Java
Tas et pile en Java Java divise la mémoire en deux types : l'un est la mémoire de pile et l'autre est la mémoire de tas.
Certaines variables de type de base et variables de référence d'objet définies dans la fonction sont allouées dans la mémoire de pile de la fonction. Lorsqu'une variable est définie dans un bloc de code, Java alloue de l'espace mémoire pour la variable sur la pile. Lorsque la portée de la variable est dépassée, Java libère automatiquement l'espace mémoire alloué à la variable, et l'espace mémoire peut être immédiatement récupéré. utilisé. Utilisez-le à d’autres fins.
La mémoire tas est utilisée pour stocker les objets et les tableaux créés par new. La mémoire allouée dans le tas est gérée par le garbage collector automatique de la machine virtuelle Java. Une fois qu'un tableau ou un objet est généré dans le tas, vous pouvez également définir une variable spéciale dans la pile. La valeur de cette variable est égale à la première adresse du tableau ou de l'objet dans la mémoire du tas. Cette variable spéciale dans la pile devient. Après avoir obtenu la variable de référence du tableau ou de l'objet, vous pouvez utiliser la variable de référence dans la mémoire de la pile pour accéder au tableau ou à l'objet dans le tas du programme. La variable de référence est équivalente à un alias ou un nom de code pour le tableau ou l'objet. .
Les variables de référence sont des variables ordinaires. La mémoire est allouée sur la pile lorsqu'elle est définie. Les variables de référence sont libérées en dehors de la portée lors de l'exécution du programme. Le tableau et l'objet lui-même sont alloués dans le tas Même si le programme s'exécute en dehors du bloc de code où se trouve l'instruction utilisant new pour générer le tableau et l'objet, la mémoire tas occupée par le tableau et l'objet lui-même ne sera pas libérée. Le tableau et l'objet ne seront pas pointés par les variables de référence lorsqu'ils sont utilisés, ils deviennent des déchets et ne peuvent plus être utilisés, mais ils occupent toujours de la mémoire et seront libérés par le garbage collector à un moment indéterminé ultérieurement. C'est aussi la principale raison pour laquelle Java occupe plus de mémoire. En fait, les variables de la pile pointent vers des variables de la mémoire tas, qui sont des pointeurs en Java !
Tas et pile en Java
Java divise la mémoire en deux types : l'une est la mémoire de pile et l'autre est la mémoire de tas.
1. La pile et le tas sont des emplacements utilisés par Java pour stocker des données dans Ram. Contrairement à C, Java gère automatiquement la pile et le tas, et les programmeurs ne peuvent pas définir directement la pile ou le tas.
2. L'avantage de la pile est que la vitesse d'accès est plus rapide que celle du tas, juste derrière les registres directement situés dans le CPU. Mais l’inconvénient est que la taille et la durée de vie des données stockées dans la pile doivent être déterminées et qu’il y a un manque de flexibilité. De plus, les données de la pile peuvent être partagées. L'avantage du tas est qu'il peut allouer dynamiquement la taille de la mémoire et qu'il n'est pas nécessaire d'indiquer la durée de vie au compilateur à l'avance. Le garbage collector de Java collectera automatiquement les données qui ne sont plus utilisées. Mais l'inconvénient est qu'en raison de la nécessité d'allouer dynamiquement de la mémoire au moment de l'exécution, la vitesse d'accès est lente.
3. Il existe deux types de types de données en Java.
L'un est les types primitifs, il y a 8 types au total, à savoir int, short, long, byte, float, double, boolean, char (notez que
n'a pas de type de chaîne de base). Ce type de définition est défini sous la forme int a = 3 ; long b = 255L et est appelé variable automatique. Il convient de noter que les variables automatiques stockent des valeurs littérales, et non des instances de classes, c'est-à-dire qu'elles ne sont pas des références à des classes. Il n'y a pas de classe ici. Par exemple, int a = 3; où a est une référence pointant vers le type int,
pointe vers la valeur littérale 3. Les données de ces valeurs littérales peuvent être connues car leur taille et leur durée de vie sont connues (ces valeurs littérales sont définies de manière fixe dans un certain bloc de programme, et les valeurs de champ disparaissent après la sortie du bloc de programme
).
Hors poursuite La raison de la vitesse existe dans la pile.
De plus, la pile a une particularité très importante, c'est-à-dire que les données stockées dans la pile peuvent être partagées. Supposons que nous définissions en même temps :
int a = 3;
int b = 3;
Le compilateur traite d'abord int a = 3 ; sera sur la pile Créez une référence à la variable a dans , puis recherchez s'il existe une adresse avec une valeur littérale de 3. Si elle n'est pas trouvée, ouvrez une adresse pour stocker la valeur littérale de 3, puis pointez a vers l’adresse de 3. Ensuite, traitez int b = 3 ; après avoir créé la variable de référence de b, puisqu'il y a déjà une valeur littérale de 3 sur la pile, b pointera directement vers l'adresse de 3. De cette façon, il existe une situation où a et b pointent tous deux vers 3 en même temps.
Il est important de noter que la référence de cette valeur littérale est différente de la référence de l'objet de classe. Supposons que les références de deux objets de classe pointent vers le même objet en même temps. Si une variable de référence d'objet modifie l'état interne de l'objet, alors l'autre variable de référence d'objet reflétera immédiatement le changement. En revanche, la modification de la valeur d’une référence littérale n’entraîne pas également la modification de la valeur d’une autre référence au littéral. Comme dans l'exemple ci-dessus, après avoir défini les valeurs de a et b, nous définissons alors a=4 ; alors, b ne sera pas égal à 4, mais toujours égal à 3. À l'intérieur du compilateur, lorsqu'il rencontre a=4;, il recherchera à nouveau s'il y a une valeur littérale de 4 sur la pile. Sinon, il rouvrira une adresse pour stocker la valeur de 4 si elle existe déjà. , il pointera directement a vers cette adresse . Par conséquent, les changements dans la valeur de a n’affecteront pas la valeur de b.
L'autre concerne les données de classe wrapper, telles que Integer, String, Double et d'autres classes qui encapsulent les types de données de base correspondants. Tous ces types de données existent dans le tas. Java utilise l'instruction new() pour indiquer explicitement au compilateur qu'elles seront créées dynamiquement selon les besoins au moment de l'exécution, c'est donc plus flexible, mais l'inconvénient est que cela prend plus de temps.
En JAVA, il existe six endroits différents où les données peuvent être stockées :
1. Il s’agit de la zone mémoire la plus rapide car elle est située à un endroit différent des autres zones mémoire : à l’intérieur du processeur. Cependant, le nombre de registres est extrêmement limité, les registres sont donc alloués par le compilateur en fonction des besoins. Vous n'avez aucun contrôle direct et vous ne ressentez aucune trace de l'existence du registre dans le programme.
2. Situé dans la RAM à usage général, mais pris en charge par le processeur via son « pointeur de pile ». Si le pointeur de pile se déplace vers le bas, une nouvelle mémoire est allouée ; s'il se déplace vers le haut, cette mémoire est libérée. Il s’agit d’une méthode rapide et efficace d’allocation de stockage, juste derrière les registres. Lors de la création d'un programme, le compilateur JAVA doit connaître la taille exacte et la durée de vie de toutes les données stockées sur la pile, car il doit générer le code approprié pour déplacer le pointeur de la pile de haut en bas. Cette contrainte limite la flexibilité du programme, donc bien que certaines données Java soient stockées sur la pile - en particulier les références d'objets, les objets Java n'y sont pas stockés.
3. Un pool de mémoire universel (également présent dans la RAM) utilisé pour stocker tous les objets JAVA. L'avantage de la différence entre le tas et la pile est que le compilateur n'a pas besoin de savoir quelle quantité de zone de stockage allouer à partir du tas, ni combien de temps les données stockées survivront dans le tas. Par conséquent, il existe une grande flexibilité dans l’allocation du stockage sur le tas. Lorsque vous devez créer un objet, il vous suffit d'écrire une simple ligne de nouveau code. Lorsque cette ligne de code est exécutée, le stockage sera automatiquement alloué dans le tas. Bien entendu, cette flexibilité doit être payée avec le code correspondant. L'allocation de stockage à l'aide du tas prend plus de temps que le stockage à l'aide de la pile.
4. Stockage statique. « Statique » signifie ici « dans une position fixe ». Le stockage statique stocke les données qui existent pendant l'exécution du programme. Vous pouvez utiliser le mot-clé static pour identifier des éléments spécifiques d'un objet comme étant statiques, mais l'objet JAVA lui-même n'est jamais stocké dans un espace de stockage statique.
5. Stockage constant. Les valeurs constantes sont généralement stockées directement dans le code du programme, ce qui est sûr car elles ne peuvent jamais être modifiées. Parfois, dans les systèmes embarqués, la constante elle-même sera séparée des autres parties, donc dans ce cas, vous pouvez choisir de la mettre dans un stockage ROM
6. Si les données survivent complètement en dehors du programme, elles peuvent alors exister sans aucun contrôle du programme et peuvent exister même lorsque le programme n'est pas en cours d'exécution.
En termes de vitesse, il existe la relation suivante :
Register< Stack 『Le paragraphe ci-dessus est extrait de "Penser en Java" 』 Première question : Question deux : Question 3 : Certaines variables de type de base et variables de référence d'objet définies dans la fonction sont allouées dans la mémoire de pile de la fonction. Lorsqu'une variable est définie dans un bloc de code, Java alloue de l'espace mémoire pour la variable sur la pile. Lorsque la portée de la variable est dépassée, Java libère automatiquement l'espace mémoire alloué à la variable. l'espace mémoire peut être immédiatement réutilisé. La mémoire tas est utilisée pour stocker les objets et les tableaux créés par new. La mémoire allouée dans le tas est gérée par le garbage collector automatique de la machine virtuelle Java. Après avoir généré un tableau ou un objet dans le tas, vous pouvez également définir une variable spéciale dans la pile afin que la valeur de cette variable dans la pile soit égale à la première adresse du tableau ou de l'objet dans la mémoire tas, cette variable dans la pile devient une variable de référence du tableau ou de l'objet. Une variable de référence équivaut à donner un nom à un tableau ou à un objet. À l'avenir, vous pourrez utiliser la variable de référence dans la pile pour accéder au tableau ou à l'objet dans le tas du programme. Le tas de Java est une zone de données d'exécution à partir de laquelle les objets de classe allouent de l'espace. Ces objets sont créés via des instructions telles que new, newarray, anewarray et multianewarray, et ils ne nécessitent pas que le code du programme soit explicitement publié. Heap Il est responsable du garbage collection. L'avantage du tas est qu'il peut allouer dynamiquement la taille de la mémoire, et la durée de vie n'a pas besoin d'être indiquée au compilateur à l'avance, car il alloue dynamiquement la mémoire au moment de l'exécution, et le garbage collector de Java le fera automatiquement. collecter ces inutilisés L'inconvénient est que la mémoire doit être allouée dynamiquement au moment de l'exécution, donc la vitesse d'accès est lente. 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。 栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义: int a = 3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。 String是一个特殊的包装类数据。可以用: 两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。 比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。 可以看出str1和str2是指向同一个对象的。 用new的方式是生成不同的对象。每一次生成一个。 因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。 另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。(不一定,因为如果事先没有,那么就会创建,这就是创建对象了,如果原来就有,那就指向那个原来的对象就可以了)!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 更多Java 堆内存与栈内存详细介绍相关文章请关注PHP中文网!String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
System.out.println(s3.equals(s4));//true
Pour être précis : la pile et le tas sont des emplacements utilisés par Java pour stocker des données dans Ram. Contrairement à C, Java gère automatiquement la pile et le tas, et les programmeurs ne peuvent pas définir directement la pile ou le tas.
int b = 3; String str = new String("abc");
String str = "abc";
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。 String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false