每次看相关技术文档,一遇到堆栈什么的总是一脸懵逼,这次决定搞清楚这两个东西,然后愉快的去装逼(手动滑稽)
先附上虚拟机在运行的时候内存分布
1.寄存器:程序无法操控。
2.栈:存放基本类型的数据和对象引用。对象本身在堆中,这里只是存放对象的引用。
3.堆:存放对象(new出来的全部放在这里面)
4.静态域:存放类中定义的static静态成员信息
JAVA内存分配中的栈:
在函数中定义的一些基本类型的数据和对象的引用变量都会在函数的栈内存中分配,当在一段代码中定义一个变量的时候,java就在栈中为这个变量分配内存空间,当该变量退出作用域的时候,java会自动释放掉为该变量分配的内存空间,释放出来的内存空间又可以被其他地方使用。(优点存取速度非常快,仅次于寄存器,数据可共享,缺点:存在栈中的数据大小与生存期必须是确定的)
JAVA内存分配中的堆:
堆内存用来存放new出来的对象和数组,堆中分配的内存由JAVA的垃圾回收机制来管理。(优点运行时动态分配内存,缺点存取速度较慢)
举几个例子:
1.main()
{ int x = 1}
2.test()
{ int x = 2 }
上述程序执行过程
第一步:main()函数程序入口,jvm执行,在栈内存中开辟一块空间存放int类型的x,并赋值为1.
第二步:程序执行到test()函数,在栈内存中再开辟一块空间存放int类型的x,并赋值为2.
此时main函数和test函数各自有独立的方法空间,各自存放自己的x,互不影响,因为他们都是局部变量。
第三步:test函数执行完毕,释放x所占的栈内存空间,x消失,由于main函数空间还在所以main函数中的x不会消失。
test函数执行结束后:
1.int [] a = new int[3];
2.a[0] = 1;a[1] = 2; a[2] = 3;
上述程序执行过程
jvm执行main()函数,在栈内存中开辟出一块空间用来存放int类型的变量a,然后在堆内存中开辟一块空间存放new int[3]数组对象,堆内存会自动内存首地址值,如0x0045。然后把首地址复制给栈内存中的x,这样x就指向堆内存的数组对象了。(栈内存中的变量指向堆内存中的对象,这就是java中的指针),此时堆内存中的数组还没有被赋值,默认值都是0,然后将堆内存中的数组对象赋值。(堆中的对象都是有默认初始值的,而栈内存中的变量都是没有默认值的,需要初始化),让我们来看一下执行图示过程。
接着上面的我们再来一个式子
x = null;
当执行这一步的时候,相当于把x指向数组的地址删掉,这样x就不在指向堆中的数组了,那相当于堆中的数组不存在被引用了,那么稍后数组将由垃圾处理机制管控处理。(不定时自动删除)
int [] a = new int[3],
int []b = a;
b[0] = 1,b[1] = 2,b[2] = 3
a = null
上述程序执行过程:
第一步不用解释了,第二步相当于我们把堆内存中数组的首地址也给了栈内存中变量b一份,b也指向堆内存中的数组,最后我们删除a中存放的数组的堆内存地址,但是这个时候b不受影响,还是指向堆内存的数组,所以数组对象依然存在。
Car c=new Car;
c.color=“blue”;
Car c1=new Car;
c1.num=5;
每一次new都会在堆中产生不同的实体
三、栈和堆的特点
栈:
函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
栈内存中的数据,没有默认初始化值,需要手动设置。
堆:
堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一(C++中需要程序员手动清除)。
注:
什么是局部变量:定义在函数中的变量、定义在函数中的参数上的变量、定义在for循环内部的变量