一、Java虚拟机内存区组成图
1、程序计数器:
- 每个线程独占一个计数器,用来指示该线程下一条要执行的指令的地址。
- 这一部分不会导致内存异常。
- PS:如果一个线程进入的是一般的Java方法,计数器指示的是下一条指令地址;如果进入的是Native方法(非Java代码)计数器为空undefined。
2、虚拟机栈:
- 线程私有,用来存储正在执行的Java方法的局部变量表、连接、方法出口等等信息。一个Java方法从开始执行到执行结束对应一个栈帧入栈到出栈的过程。
- 局部变量表是虚拟机栈中最主要的部分,局部变量表里存储了线程对应代码编译期间确定下的所有数据类型和对象引用,局部变量表的slot个数编译后就完全确定了,运行期间slot个数不会改变。
- 如果JVM提前规定了栈深度(可以简单理解为slot个数),线程需要的深度如果大于该值,会爆栈,抛出StackOverflowError异常;
- 如果JVM栈深度是动态扩展的,那一直到虚拟机栈内存全部用完,然后抛出O.O.M异常。
3、本地方法栈:
- 和虚拟机栈类似,只不过虚拟机栈为普通Java方法(字节码)服务,本地方法栈为Native方法服务。
4、堆(GC堆、“垃圾堆”):
- 所有线程共享;存放几乎所有的对象实例;
- 和前面的栈一样,Java堆既可以是固定大小的,也可以是可扩展的,一般可扩展(对应参数-Xms、-Xmx)。
- 堆空间不够抛出O.O.M异常。
5、方法区(非堆):
- 所有线程共享;用来存放一些已经被JVM加载的常量、静态变量等数据。
- 方法区空间不足时抛出O.O.M异常;
PS:程序中的常量不一定在编译器全部加入运行时常量池,运行过程中产生的常量也可以加入常量池中。代码运行过程中产生常量的例子如:String类的intern()函数,intern()函数在JDK1.7之前和之后以及JDK1.8中好像均有区别,具体内容可以参考博客:字符串的intern()方法--字符串常量池_字符intern_JnHaLee的博客-CSDN博客
6、直接内存:
资料上说直接内存不属于虚拟机运行时数据区的一部分,不是很理解前面这句话...但能确定的是在根据物理机内存确定JVM所需内存空间时要把这一部分占用的空间也考虑上。
参考资料:《深入理解Java虚拟机:... 第3版》周志明