1 概念
1.1 堆栈是java中对内存划分的概念逻辑
Java把内存划分成2种,一种是堆内存,一种是栈堆存。
堆栈是逻辑概念上的。
1.2 什么会被分配在栈中
1.2.1 函数中定义的变量
在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
1.2.2 代码块中定义的变量
当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另做他用。
1.3 什么被分配在堆中—new
堆内存,存放由new创建的对象和数组。
堆中分配的内存,有JVM的GC来管理。
1.4 声明对象与引用变量
声明对象时,就是先在栈中定义一个特殊的变量,不指向任何内存地址,然后再堆中产生了一个数组或对象后,让栈中这个变量的取值等于数组或对象在堆内存中的首地址。
引用变量,相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。
与C++不同,java自动管理堆和栈,程序员不能直接地设置栈和堆。
2 堆栈区别
2.1 数据结构上
堆和栈都是一种数据项按序排列的数据结构。
栈就像装数据的桶或箱子,是一种具有后进先出性质的数据结构。
堆,像一颗倒过来的树,是一种经过排序的树形结构,每个节点都有一个值。通常我们所说的堆的数据结构,是指二叉树。堆的特点是根节点的值最小(或最大),且根节点的两个子树也是一个堆。由于堆的这个性质,常用来实现优先队列,堆的存取的随意。
2.2 安全性
堆很灵活,但是不安全。对于对象,我们要动态的创建、销毁,不会说后创建的对象没有销毁,先前创建的对象就不能销毁。而一旦堆中的对象被销毁,我们继续引用这个对象的话,就会出现著名的NullPointerException。这就是堆的缺点—错误的引用逻辑只有在运行时才会发现。
栈不灵活,但是很严格,是安全的,易于管理。只要上面的引用没有销毁,下面引用就一定还在,所以,在栈中,上面引用永远可以通过下面引用来查找对象,同时如果确认某一区间的内容会一起存在、一起销毁,也可以上下互相引用。在大部分程序中,都是先定义的变量、引用先进栈,后定义的后进栈,同时,区块内部的变量、引用在进入区块时压栈,区块结束时出栈,理解了这种机制,就可以很方便理解各种编程语言的作用域的概念了,同时这也是栈的优点—错误的引用逻辑在编译时就可以被发现。
2.3 效率灵活性
Java堆是一个运行时数据区,类的对象从中分配空间,这些对象通过new等指令建立,它们不需要代码程序来显式的释放,而是由垃圾回收来负责。
堆的优势:可以动态分配内存大小,生存期也不必事先告诉编译器,因为它在运行时动态分配内存,java 的GC会自动收走这些不再使用的数据。
缺点是:由于要在运行时动态分配内存,存取速度较慢。
栈的优势:存取速度比堆块,仅次于寄存器
缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
2.4 栈数据可以共享(基本类型的变量)
如 int a =3;
intb = 3;
编译器先处理int a =3; 首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3,接着处理 int b=3,在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3.这样,就出现了a,b均指向3的情况。这时,如果另a=4;那么编译器会重新搜索栈中是否有4值,如果没有,将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值改变不影响b值。
注意,这种数据共享,与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b,它是由编译器完成的,有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
转自:如何理解JAVA堆栈
http://www.sunjianbo.com/heap-and-stack.html