【Java编程思想阅读笔记】Java数据存储位置
Java数据存储位置
P46页有感
一、前置知识
- 栈是由系统自动分配的,Java程序员对栈没有直接的操作权限,
- 堆是所有线程共享的内存区域,栈 是每个线程独享的。
- 堆是由程序员自己申请的,在使用new关键字创建一个对象的时候,对象就会被分配到堆内存中。并且由于栈是由系统自动分配的,因此申请的效率和速度是高于要使用new关键字申请内存的堆。
- 栈是一块连续的区域,并且栈的大小系统确定好的,当需要的栈空间小于剩余空间时,系统就会自动分配,否则会报栈溢出的错误;而堆空间则不是一块连续的区域,如果学过操作系统课程的可以知道,操作系统中有一个记录空闲内存地址的链表,当在申请堆内存时,系统会遍历这个链表,找到第一个内存空间符合申请空间的节点地址,然后将该节点从空闲链表中删除,再把内存空间分配给程序。详细的分配过程,可以去学习操作系统课程。
二、Java的六个存储位置
2.1.寄存器
寄存器位于CPU内部,这是计算机硬件中处理速度最快的地方,是真正的处理程序的位置。寄存器是由编译器根据需要来分配的,并且即便是编译器,也需要通过操作系统的抢占式调度才能够获得CPU的处理权,因此,寄存器的存储空间时非常有限的,开发人员对寄存器没有直接的控制权,在程序里也没法对寄存器有什么操作。
2.2.堆栈
堆栈一般也叫栈,一般指的是RAM区域,处理速度仅次于寄存器。在堆栈中,一般存放的都是对象的引用,当栈指针下移的时候,就创建新的内存,反之则释放内存。当创建一个对象时。该对象的会被放到堆内存的空间,而该对象的引用则会被放到栈内存中。并且Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存
在时间”。以便回收空间和知道栈内存还剩余多少空间。
2.3.堆
堆内存也是在RAM区域,它是对象的真正存放位置,当new一个对象时,该对象就会被存放至堆内存,并且Java编译器不需要去在意堆中还剩多少空间,Java 有一个特别 的“垃圾收集器”,它会查找用new创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由 那些闲置对象占据的内存,以便能由新对象使用。这意味着我们根本不必操心内存的回收问题。只需简单地创建对象,一旦不再需要它们,它们就会自动离去。这样做可防止在C++里很常见的一个编程问题:由于程序员忘记释放内存造成的“内存溢出”,对象的销毁---对象的引用放在栈中,所以使用完引用就被从栈中销毁了,但是实际的对象仍然存放在堆中,只有在没有任何的引用使用它的时候才被垃圾回收器销毁掉。
class StaticTest {
Static int i = 47;
}
//现在我们 new两个对象
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
//st1.i和st2.i都是47 尽管我们制作了两个StaticTest对象,但它们仍然只占据StaticTest.i的一个存储空间。这两个对象都共享同样的i 此时不管是哪个对象执行st1++,另一个对象的i也会加一
2.5.常量池
常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数 需要严格地保护,所以可考虑将它们置入ROM。
2.6 非RAM存储
非RAM存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。 其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给 另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对 于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复 成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。未来的版本甚至可能提 供更完整的方案。
三、基本类型的存储位置
在Java编程思想中对于基本数据类型的存储位置有这么一句话
对于这些基本类型,Java 采纳了与C和C++相同的方法。也就是说,不是用 new创建变量,而是创建一个并非引用的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,能够更 高效地存取。
但是我百度查找了一些相关资料,也有说基本数据类型有放在堆中的。梳理一下,首先,八大基本类型都不能看做对象,基本类型的存储位置要分情况,当基本类型的创建是在类中。像这样
class Demo{
int a = 1;//情况1
public int test(){
int b = 2;//情况2
}
}
在情况1下,创建的是全局变量。是随着对象一起存放在堆中的,在情况2下,创建的是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因。由此可见,基本类型并不是完全存储在栈区的。原因是因为,上面基础知识提到的,堆是所有线程共享的内存区域,栈 是每个线程独享的。如果你将一个实例变量放在栈内,那么就不存在多个线程访问同一个对象资源了,这显然是不对的,所以全局变量要在堆上创建,也不是线程安全的。但是对于局部变量,是在栈上创建的,每一次方法调用创建一个方法栈,独享一份内存区域,其他的线程是不会访问到该线程的资源,在 栈上创建也会减轻GC的压力,随着该方法的结束,相对应的方法栈内存消除,这种局部变量占用的内存自然就消失了,因此局部变量是线程安全的。