概述
先Copy一个概念:JVM是基于堆栈的虚拟机。JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
例子分析
堆是用来存储new出来的对象,也就是真实对象的存储地方。栈是用来存储基本变量、局部变量及对象的引用的。
举个来说明吧
class User {
private int id;
private String name;
public User(int sid, String sname) {
this.id = sid;
this.name = sname;
}
}
public void test() {
int a = 10;
int b = 10;
b = 12;
String c = "hello";
String d = new String("hello");
User user = new User(10, "hello");
}
基本变量类型都是存储在栈中的,所以int型的a、b、c都存在栈中。具体的操作再来分析下:
int a = 10会检查栈中有没有10这个值,没有就创建了一个10的值,再创建了a去指向10。
int b = 10同样会检查栈中有没有10这个值,发现已经存在,则创建b指向已存在的10。在栈中,变量值是可以共享的。
b = 12检查栈中有没有12这个值,没有就创建一个12的值,再把b的指向12,这样也不会影响a的值。
说到String时,先说说JVM有一个常量存储区域,是用来存常量的,像代码定义直接双引号中的内容就是存在常量存储区域的。String不是基本变量类型。
String c = "hello"会先到常量存储区域找hello,如果没有,就在常量存储区域创建hello值,栈中创建一个c指向常量区域的hello。
String d = new String("hello")是new一String对象,会先到常量区域检查有没hello值,如果有,就在堆中生成hello的一个对象副本,如果没有就会在常量区先创建hello,再在堆中生成一个副本。在栈中生成d,指向堆中的hello对象,d是对象的引用。
User user = new User(10, "hello"),会构造函数,构造函数里的sid,sname是局部变量,会存储在栈中,指向的值是栈中的10和常量存储中的hello。在堆中会有new User这个对象,在栈中会有user这个对象的引用。这里要说一下,sid,sname是构造方法执行完后,会自动从栈中移除,而成员变量id,name是对象的属性,会在堆中存在,需要等JAVA自动回收机制进行回收。
当执行完test方法后,栈中的基础变量和值及对象引用都会被自动移除,释放内存。
通过例子应该可以比较好理解哪些数据存在堆中,哪些数据存在栈中了。
总结
堆分配内存时,大小和生存期都是不确定的,要生成的时候才知道,遵循先进先出的原则,读取速度比较慢。数据不能共享,每new一次都是一个新的对象。要由JAVA的垃圾回收机制来回收,所以容易造成内存多高。
栈分配内存时数据的大小和生存期是确定的,在编译时就知道了,遵循先进后出的原则,读取速度快,接近寄存器了。数据可以共享,节省内存,也比较快。在生存期结束后会自动释放,效率更高。