java的数据存储区域有六种:
(1)寄存器:
寄存器是最快的存储区,这是因为它位于不同于其它存储区额地方——处理器的内部。但是寄存器的数量十分有限,所以是由编译器根据需求进行自动分配的,不能由程序来控制。
(2)栈(Stack):
栈的速度仅次于寄存器,因为它位于RAM中。栈中存放基本数据类型的变量数据和对象的引用。由于栈中需要频繁地上下移动指针,此时便需要确切地知道每个对象的生命周期,这一约束便限制了程序的灵活性,所以对象的本身并不会放在栈中,而是会放在堆中,因为堆中的对象是由垃圾回收器来回收的,具有比较大的灵活性;
(3)堆(Heap):
位于通用的内存池(也位于RAM区),用于存放所有的对象(简单讲就是new出来的对象);
(4)静态域:
存放所有的静态成员(static变量);
(5)常量池:
存放字符串常量(String变量,因为String是immutable)和基本数据类型常量(final int等变量)。有时在嵌入式系统中,常量本身会和代码部分相对独立开来,这样的情况下,可以将其存放在ROM中;
(6)非RAM存储:
硬盘等永久存储区域,此时数据是完全存活于程序以外的。
针对比较常用的栈,堆和常量池,需要理解清楚。对于栈和常量池中的数据是共享的,而堆中的数据是不可共享额。栈中的数据项的生命周期是可以且必须是确定的,所以当没有引用指向该数据时,这个数据项就会从栈中消失。而堆中的对象则是有垃圾回收器来负责回收的,因此其生命周期不需要确定,具有比较大的灵活性。
针对常用类型的详细理解:
对于字符串,其对象的引用存储在栈中,但是如果是在编译期间就已经确定的(即直接是双引号赋值),那就会存在常量池中,而如果是在运行期间(即new出来的)才能确定的就存储在堆中。在常量池中,始终只会有一份值,而在堆中,可能会有多个。
示例:
String s1 = “china”;
String s2 = “china”;
String s3 = “china”;
String ss1 = new String(“china”);
String ss2 = new String(“china”);
String ss3 = new String(“china”);
对于这段代码,其存储结构是这样的:
这边黄色的箭头意义: 对于通过new产生的字符串(假设为“china”),首先会去常量池中查找是否存在这样的字符串“china”,如果没有就会在常量池中创建一个此字符串对象,然后再在堆中创建一个常量池中该“china”对象的拷贝对象。所以对于一道面试题:String s = new String(“x”);产生几个对象?答案是一个或者两个,因为当常量池中有值为“x”的对象,那么只会在堆中创建该对象的一个拷贝对象,如果没有的话,那就是先在常量池中创建一个值为”x”的对象,然后在堆中继续创建该对象的一个拷贝对象,就是一共创建了两个对象。
对于基础数据类型的变量和常量:
变量及其引用均存储在栈中,常量存储在常量池中。
示例:
int i1 = 9;
int i2 = 9;
int i3 = 9;
static final int INT1 = 9;
static final int INT2 = 9;
static final int INT3 = 9;
存储结构:
对于成员变量和局部变量:
成员变量存储在堆中的对象里面(因为成员是属于某个对象的),由垃圾回收其负责回收;
局部变量存储在栈中,因为局部变量一般都是方法内部的形式参数,所以会随着方法的消失而消失。
示例:
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
省略get,set方法………
}
public class Test{
public static void main(String args[]){
int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
}
public void change(int i){
i = 1234;
}
}
存储过程结构:
详细分析上述代码执行过程:
<1>. main方法开始执行 : int date = 9;
由于date是局部变量(出在main函数内,而非函数外&类中),且位基本数据类型,则引用(date)和数值(9)都在栈中;
<2>. Test test = new Test();
test位对象的引用,存在栈中,对象(new Test())存在堆中;
<3>. test.change(date);
观察change函数,i为局部变量,存在栈中,当change函数执行完毕,i从栈中消失;
<4>. BirthDate d1 = new BirthDate(7,7,1970);
d1为对象的引用,存在栈中,对象(new BirthDate())存在堆中,其中后面的d,m,y均为形式参数,属于局部变量,存在栈中,并且由于它们为基本数据类型,所以其数值也都存在栈中。而day,month,year是属于成员变量,它们则存储在堆中(其实是存在堆中的new BirthDate()对象),当执行完其构造方法后d,m,y会从栈中消失,而day,month,year则会伴随垃圾回收器回收new BirthDate()对象时才会一并被回收。
<5>. main方法执行完毕
date变量,test,d1引用都从栈中消失,new Test(),new BirthDate()对象将会等待垃圾回收器回收释放。