java变量在内存分配原理
程序变量如何在内存分配的?其值又存放在哪里?下面以java为语言基础讲解
一、基本原理
例:int a = 10;
boolean b = false;
上面例子有两行语句,分别定义了变量a(int型,在32位系统中是4位字节),b(布尔型,只占1个字节)。都是基本类型,占位长度确定,内存分配如下:
|
内存地址 |
值 |
|
0x01 |
10 |
|
0x02 |
|
|
0x03 |
|
|
0x04 |
|
|
0x05 |
false |
上面表格代表内存分布图,每一行代表1个字节,10是int型,4个字节,故占用4个地址,变量a只存首地址,然后根据长度就能找到以下所有内存地址(连续取4个字节),就能取对应的值了。false是1个字节,故只占一行。
java编译器编译时,会对所有变量存入一个叫符号表,符号表就是变量映射为值内存地址的表。如下
|
变量名 |
内存地址 |
|
a |
0x01 |
|
b |
0x05 |
java解释器在运行时,会把符合表中所有变量名都替换为实际内存地址,这样就可以直接找到真正内存值了。
二、java堆栈中内存如何分配和释放?
在前一篇已提到,java堆栈是由java堆栈指针上下移动操作就代表了创建/释放内存空间。还是举个例子说明:
void fun() {
int a = 12;
int b = a;
int c = 12;
int d = 13;
}
内存分配如下图:
内存地址表:
|
内存地址 |
值 |
|
0X01 |
12 |
|
0X02 |
|
|
0X03 |
|
|
0X04 |
|
|
0X05 |
13 |
|
0X06 |
|
|
0X07 |
|
|
0X08 |
符号表:
|
变量名 |
内存地址 |
|
a |
0X01 |
|
b |
0X01 |
|
c |
0X01 |
|
d |
0X05 |
工作原理:
1。int a = 12; 在堆栈里查找是否有包含12的值,如有包含则直接返回12的内存地址,如无包含则新创建该值,堆栈指针往上移,并返回12的引用地址(0X01)。
2。int b = a; 直接将符合表里的a内存地址赋于b。
3。int c = 12; 在堆栈里查找是否有包含12的值,已经找到则返回12引用地址。
4。int d = 13; 在堆栈里查找是否有包含13的值,没找到则新创建并返回内存地址。
当执行到方法最后},代表该方法生命周期结束,方法内所有变量应该释放,释放过程是堆栈指针往下移,先释放d,再释放c,再到最后a。从上面过程可看出,堆栈值具有共享特性,它的值只会存一份,这也节省了内存空间。另外当变量要修改值时,如a = 20;先会找堆栈里是否有20值,如有则把引用地址替换到符号表a的内存地址里,这也不会干预到其他变量的引用。
三、java堆中内存如何分配和释放?
java堆是存放引用类型的,创建一个引用类型实例,实质是创建了两个地址空间,一个是存放对象(主要是存类字段)本身的堆里,一个是存放对象的引用地址,放在堆栈里,例:
Object o1 = new Object();
Object o2 = new Object();
Object o3 = o1;
内存分布图:
堆区
|
内存地址 |
对象 |
|
0X01 |
O1的实质内容 |
|
0X02 |
O2的实质内容 |
栈区
|
变量名 |
内存地址 |
|
O1 |
0X01 |
|
O2 |
0X02 |
|
O3 |
0X01 |
符号表跟上面一样,就不画了,直接在栈区里用变量名代替,工作过程如下:
1。Object o1 = new Object(); 在堆里创建Object对象并分配内存空间,再在堆栈里创建变量o1,并存放刚堆里创建的引用地址(0X01)。
2。Object o2 = new Object(); 在堆里创建Object对象并分配内存空间,再在堆栈里创建变量o2,并存放刚堆里创建的引用地址(0X02)。
3。Object o3 = o1; 在堆栈里创建变量o3,并存放o1的引用地址。
由上面可得,如更改o1的属性,则同样会更改o3,因为它们两都指向同一对象,这跟上面更改堆栈里的值不一样,堆栈里的值是原子操作,相当于一个副本,互不干扰。
当执行完生命周期结束后,会立即释放堆栈里的空间,也就是o1,o2,o3里存放的引用地址会清零,而堆里的实质对象不会释放,要等到GC工作后才会释放,GC确定释放前,会执行每个对象的finalize()方法,该方法是Object继承来的,java所有类都继承Object根类。关于GC工作原理留在后续讲。
浙公网安备 33010602011771号