2019独角兽企业重金招聘Python工程师标准>>>
首先我们来说说内存,因为从内存的角度来出发来分析一些变量,引用或者对象的生命周期会更好理解一些。
java是一门编程语言,他跟C++有什么不同呢?本质上,他们都是一样的,殊途同归。因为底层都是操作内存和磁盘的工具,只是语法不同,表现形式不同而已。
所以有的时候不必刨根问底,因为这可能只是语言的一种规则而已。
java中都是以对象和对象的引用形式来操作数据的,那么java中是如何分配这些存储引用和内存的呢?
一、 java 中的内存块
1. 寄存器。这是最快的存储区,以为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。
2. 栈(又名堆栈),原型就是碟盘子模型,先放的盘子后拿。栈位于通用RAM(随机访问存储器)中,但通过堆栈指针可以直接从处理器那里获得直接支持。通过栈顶指针的上下移动来创建和释放内存,这是一种快速有效的分配存储的方法,仅次于寄存器。但是这种方式,java系统必须知道存储在堆栈内所有项的生命周期,以便上下移动堆栈指针。所以对象的引用存在与堆栈中(因为由确切的生命周期),但是对象不会存于堆栈中(无确切的生命周期)。
3.堆。一种通用的内存池,也是位于RAM区,用于存放所有的java对象。堆不同与堆栈的好处是:编译器不需要知道存储的数据在堆里存活多长时间。因此,在堆里分配存储由很大的灵活性。当需要一个对象时,只需new一个,当执行时,会自动在堆里进行分配内存。当然,为了这种灵活性必须付出相应的代价:用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间。(对比于C++,对象会在栈中存储,因为对象的生命周期都是程序员自己控制的)
4.常量存储。常量值通常直接存放在程序代码的内部。
5.非RAM存储。如果数据完全存活于程序之外,那么可以不受程序的任何控制,在程序没有运行时也可以存在。其中两个基本的例子就是流对象和持久化对象。在流对象中,对象转化成字节流,通常被发送给另一台机器。在持久化对象中,对象被存放于磁盘上,因此,即使程序终止,他们仍可以保持自己的状态。这种存储方式的技巧在于:把对象转化成为可以存储在其他媒介上的事物,在需要时,可恢复成常规的、基于RAM的对象。java提供了对轻量级持久化的支持。
二、基本类型在栈中
java对于小的,基本的变量是这样处理的:
将他们固定长度,直接存储值于堆栈中,所以java的基本类型都是有固定取值范围的。
java基本类型长度不变也是java的可移植性的重要原因。
三、对象的生命周期
变量的生命周期往往是由大括号决定的,而对象则不然
{
String s = new String("a string");
}
在上面的这段代码中,引用s在作用域终点就消失了。然而,s指向的String对象仍然继续占据着内存空间。
我们已经无法在作用与之外访问这个对象,因为他唯一的引用已经失效了。
事实证明,由new创建的对象,只要你需要,就会一直保留下去。
那么他们什么时候消亡,以至于不会填满内存呢?这就引出了垃圾回收器。
垃圾回收器的作用:监视new创建的所有对象,并辨识哪些对象是不会再被引用的对象,随后,释放这些对象的内存空间,以便供其他新的对象使用。所以对象不需要程序员自己回收。神奇的GC...
三、有关static
class StaticTest {
static int i = 47;
}
即使你创建了两个StaticTest对象,StaticTest.i也只有一份存储空间,这两个对象共享一个i。
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
在这里,st1.i和 st2.i指向同一存储空间,因此他们具有相同的值47.
所以static字段对每个类来说只有一份存储空间,而非static字段则是对每个对象有一个存储空间。