Java中的栈、堆和常量池
Java程序是运行在JVM(Java虚拟机)上的,因此Java的内存分配是在JVM中进行的,JVM是内存分配的基础和前提。
Java程序的运行会涉及以下的内存区域:
-
寄存器:JVM内部虚拟寄存器,存取速度非常快,程序不可控制。
-
栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中。
-
堆:存放new出来的对象,注意创建出来的对象只包含各自的成员变量,不包括成员方法。
-
常量池:存放常量,如基本类型的包装类(Integer、Short)和String,注意常量池位于堆中。
-
代码段:用来存放从硬盘上读取的源程序代码。
-
静态域:用来存放static修饰的静态成员。
下图表示了程序大致的内存分配情况:
1、声明和实例化
声明:A a = null; 声明一个A类的对象a,a放在栈中。
实例化:a = new A(); 实例化这个对象a,new A() 放在堆中。
String s = newString("a"); 创建了两个对象,一个是在常量池中,一个是在堆内存中,常量池的为"a",堆内存中为new String()。变量s指向该new string()对象,而该对象又指向在常量池中的字符串常量”a”。注意的是,在new的时候java虚拟机先去内存的常量池中查找”a”这个对象,如果有就不创建了,直接把堆中的对象指向该字符串。
String s = "a"; 的意思就是在栈中创建一个字符串类型的变量s,并且变量s直接指向常量池中的字符串对象”a”,省去了中间的堆内存中的对象。注意的是,String s = "a",这行代码被执行的时候,java虚拟机首先在字符串池中查找是否已经存在了值为"a"这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
2、数据存放形式
String s1 = "china";
String s2 = "china";
String s3 = "england";
String s4 = new String("china");
String s5 = new String("england");
对于字符串,其对象的引用(这里指:s1,s2,s3,s4,s5)都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期new出来的(一旦new就会开辟新的堆内存)才存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
3、数据存放位置
int s1=6; //s1存放在栈中;6存放在常量池
char s2 = 'g'; //s2存放在栈中;'g'存放在常量池
boolean flag = true; //flag存放在栈中;true存放在常量池
byte b = 6; //b存放在栈中;6存放在常量池
String s3 = "china"; //s3存放在栈中;"china"存放在常量池
String s4= new String("abc"); //s4存放在栈中;new String()存放在堆中;"abc"存放在常量池中
注意:
栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步