java中的数据存储结构

java中的数据存储结构

在java中数据的存储结构可以分为六种:

寄存器存储区 堆栈区 堆区 静态存储区 常量存储区 非RAM数据存储区

寄存器存储区

由于位于处理器cpu的内部,所以其是最快的存储区,然而寄存器的空间极其珍贵有限,所以程序开发者无法直接控制寄存器空间的分配,它的分配工作是由编译器自行分配的.

特点:最快的存储区, 由编译器根据需求进行分配,开发者在程序中无法进行控制.

堆栈存储区

位于通用RAM中,即内存中。它可以通过”堆栈指针”从cpu那里获取支持,通过操作”堆栈指针”的上下移动来实现堆栈区中对内存的申请和释放.堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。这是一种快速有效的分配存储方法,速度仅次于寄存器.

创建程序时候,JAVA编译器必须知道存储在堆栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些JAVA数据存储在堆栈中——特别是对象引用,但是JAVA对象不存储其中.

特点:存放基本类型的变量数据和对象,数组的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中).
@Test public void stack(String msg){ //调用方法是在栈内存中为参数msg分配存储空间,方法结束自动释放。 int count=0; //count是方法中的局部变量,存储在栈内存中(基于效率的考虑, 基本类型数据的引用和值都存放在栈内存中),方法结束时候,释放内存 LoginPResenterTest presenterTest=new LoginPresenterTest();//局部变量是复杂数据类型LoginPresenterTest,对象的引用presenterTest存放在栈内存中,而对象的创建发生在堆内存中. }

堆区

跟堆栈区一样也存在于RAM中,用于存放所有的JAVA对象。堆不同于堆栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要创建一个对象的时候,只需要new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代码。用堆进行存储分配比用堆栈进行存储存储需要更多的时间.

特点:存放所有new出来的对象,而针对堆中对象内存的回收依赖于GC.

静态存储区

存储用static修饰的成员变量或方法.虽然static关键字可以用来标识一个对象是静态的,但JAVA对象本身不会被存放在静态存储空间里,而是存储在堆内存区.

特点:存放static修饰的静态成员(方法或变量)

常量存储区

常量(字符串常量和基本类型常量)通常直接存储在程序代码内部(常量池)。这样做是安全的,因为它们的值在初始化时就已经被确定,并不会被改变。常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = “abc”这种字面量方式.

特点:存放字符串常量和基本类型常量(public static final).

非RAM数据存储区

如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序没有运行时也可以存在.

特点:硬盘等永久存储空间.

堆与堆栈的关系:

这是一个关于堆与栈关系的引用说明

堆:堆是heap,是所谓的动态内存,其中的内存在不需要时可以回收,以分配给新的内存请求,其内存中的数据是无序的,即先分配的和随后分配的内存并没有什么必然的位置关系,释放时也可以没有先后顺序。一般由使用者*分配,在C语言中malloc分配的就是堆,需要手动释放。 堆栈:就是stack。实际上是只有一个出入口的队列,即后进先出(frist in , last out),先分配的内存必定后释放。一般由,由系统自动分配,存放函数的参数值,局部变量等,自动清除。 还有,堆是全局的,堆栈是每个函数进入的时候分一小块,函数返回的时候就释放了,静态和全局变量,new得到的变量,都放在堆中,局部变量放在堆栈中,所以函数返回,局部变量就全没了。 JAVA中的基本类型,其实需要特殊对待。因为,在JAVA中,通过new创建的对象存储在“堆”中,所以用new 创建一个小的、简单的变量,如基本类型等,往往不是很有效。因此,在JAVA中,对于这些类型,采用了与C、C++相同的方法。也就是说,不用new 来创建,而是创建一个并非是“引用”的“自动”变量。这个变量拥有它的“值”,并置于堆栈中,因此更高效。 实际上类的实例方法在内存中是只有一份,不过肯定不会是第一个对象中,如果是第一个对象的话,那么当第一个对象被销毁的时候,那么后面的对象就永远无法调用了。 类的实例方法存在一个专门的区叫方法区(method area),事实上类刚装载的时候就被装载好了,不过它们在”睡眠”,只是这些方法必须当有对象产生的时候才会”苏醒”.(比如,一个输出类的成员变量的方法,如果连对象都没有,何来的输出成员变量).所以,方法在装载的时候就有了,但是不可用,因为它没有指象任何一个对象. 而静态的又不一样了,静态的东西存在静态存储(static storage)区,他们和类是一个等级的,就是说只要类被装载,它们就可以直接用.(用类名来调用).他们不依赖与任何对象,所以也不能输出任何对象的成员属性.(除非成员属性也是静态的). 每个对象在new的时候都会在堆区中开辟内存,用来保存对象的属性和方法.(实际上方法保存的只是方法区的引用,如果保存的是方法本身,那么试想一下,有多少个对象就得有多少个方法,那么又和第一点中”实例方法在内存中只有一份拷贝”相矛盾了)。


栈内存中存储的数据可以实现数据共享

假设我们定义了两个局部变量(注意局部变量和方法形参才保存在栈中,成员变量是保存在堆中,不要混淆了) int s1=20;int s2=20;编译器工作流程如下:

在栈中申请一个名为”s1”的存储空间,然后查看栈中是否存放着一个”20”的值; 如果栈中不存在”20”的值,就向栈中添加一个”20”的值,然后让s1等于这个值. 如果栈中存在”20”的值,就让s1等于这个值而不用在栈中重新加入一个”20”的值. 完成了对s1的数据分配后,由于s2=20,所以就不用再为s2在栈中重新分配”20”这个值了,这样就实现了数据共享,节省了内存空间.