JVM中的堆和栈到底存储了什么
JVM数据区
先上⼀张Java虚拟机运⾏时数据区中堆、栈以及⽅法区存储数据的概要图,如下所⽰:
然后我们来具体解析⼀下堆和栈
堆
堆是存储时的单位,对于绝⼤多数应⽤来说,这块区域是 JVM 所管理的内存中最⼤的⼀块。线程共享,主要是存放对象实例和数组。
栈
栈是运⾏时的单位,Java 虚拟机栈,线程私有,⽣命周期和线程⼀致。描述的是 Java ⽅法执⾏的内存模型:每个⽅法在执⾏时都会创建⼀个栈帧(Stack Frame)⽤于存储局部变量表、操作数栈、动态链接、⽅法出⼝等信息。每⼀个⽅法从调⽤直⾄执⾏结束,就对应着⼀个栈帧从虚拟机栈中⼊栈到出栈的过程。
局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引⽤(reference 类型)和returnAddress 类型(指向了⼀条字节码指令的地址)
堆和栈的对⽐
⼀、栈解决程序的运⾏问题,即程序如何执⾏,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪⼉。
⼆、栈因为是运⾏单位,因此⾥⾯存储的信息都是跟当前线程相关的信息。包括:局部变量、程序运⾏状态、⽅法返回值等等;⽽堆只负责存储对象信息。
三、在⽅法中定义的⼀些基本类型的变量和对象的引⽤变量都是在函数的栈内存中分配。堆内存⽤于存放由new创建的对象和数组。
四、在Java中⼀个线程就会相应有⼀个线程栈与之对应,这点保证了程序的并发运⾏。
⽽堆则是所有线程共享的,也可以理解为多个线程访问同⼀个对象,⽐如多线程去读写同⼀个对象的值
五、栈内存溢出包括StackOverflowError和OutOfMemoryError。StackOverflowError:线程请求的栈深度⼤于虚拟机所允许的深度。OutOfMemoryError:如果虚拟机栈可以动态扩展,⽽扩展时⽆法申请到⾜够的内存;堆内存溢出是OutOfMemoryError。如果堆中没有内存完成实例分配,并且堆也⽆法再扩展时,抛出OutOfMemoryError异常。
代码分析
最后,借助⽹上看到的⼀个例⼦帮助对栈内存,堆内存的存储进⾏理解:
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下⾯分析⼀下代码执⾏时候的变化:
1. main
⽅法开始执⾏:int date = 9;
date
局部变量,基础类型,引⽤和值都存在栈中。
2. Test test = new Test();
test
为对象引⽤,存在栈中,对象(new Test())存在堆中。
3. test.change(date);
调⽤change(int i)⽅法,i为局部变量,引⽤和值存在栈中。当⽅法change执⾏完成后,i就会从栈中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
调⽤BIrthDate类的构造函数⽣成对象。
d1为对象引⽤,存在栈中;
对象(new BirthDate())存在堆中;
其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中;
day,month,year为BirthDate对象的的成员变量,它们存储在堆中存储的new BirthDate()对象⾥⾯;
当BirthDate构造⽅法执⾏完之后,d,m,y将从栈中消失。
5.main⽅法执⾏完之后。
date变量,test,d1引⽤将从栈中消失;
new Test(),new BirthDate()将等待垃圾回收器进⾏回收。