书的内容来自《深入理解Java虚拟机》
1. 程序计数器(Program Counter Register)
简介
记录当前线程正在执行的字节码指令的地址。
- 如果一个线程正在执行一个Java方法,这个程序计数器记录的是正在执行的字节码指令的地址。
- 如果一个线程正在执行一个Native方法,这个程序计数器的值为空(Undefined)。
字节码解释器:通过改变程序计数器的值来选取下一条要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复都需依赖程序计数器来实现。
Why 线程私有?
Java多线程就是通过线程轮流切换并分配处理器执行时间的方式来实现的。在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。 因此,为了线程切换时能恢复到原来的指令执行位置,每个线程都需要独立的程序计数器。
特点
- 内存空间较小。
- 不会出现OutOfMemeryError。
- 线程私有。
2. JVM 栈(Java Virtual Machine Stack)
简介
描述Java方法执行的内存模型。 每个方法在执行的同时会创建一个栈帧(Stach Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 每个方法从调用到执行结束,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表
- 局部变量表存放编译器可知的基本数据类型、对象引用、returnAddress类型。
- 64位的long和double类型数据占用2个局部变量空间(Slot),其余数据只占用1个。
- 局部变量表所需要的空间在编译期间完成分配,当进入一个方法,这个方法在帧中分配多少局部变量空间是确定的,在方法运行期间,不会改变局部变量表的大小。
特点
- 内存空间较小;
- 线程私有,生命周期与线程相同。
- 存在StackOverflowError和OutOfMemoryError。
StackOverflowError: 线程请求的栈深度超过虚拟机允许的最大栈深度。如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存时,抛出OutOfMemoryError。
3. 本地方法栈(Native Method Stack)
简介
与虚拟机栈类似,区别在于虚拟机栈为执行Java方法(字节码)服务,而本地方法栈为执行本地方法服务。
4. Java 堆(Java Heap)
简介
- 存放对象实例。
- 垃圾回收的主要区域。
- Java堆的内存空间在物理上可不连续,逻辑上连续即可。在实现时,既可以实现成固定大小的,也可实现成可扩展的。可扩展的通过-Xmx和-Xms控制。
特点
- 内存空间最大;
- 所有线程共享,在虚拟机启动时创建。
- 没有足够的内存存放实例时,抛出OutOfMemoryError。
5. 方法区(Method Area)
简介
存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
特点
- 所有线程共享;
- 没有足够的内存存放数据时,抛出OutOfMemoryError。
运行时常量池(Runtime Constant Pool)
- 运行时常量池是方法区的一部分。Class文件除了有类的版本、字段、方法、接口等信息外,还有一项常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分在虚拟机加载后存入方法区的运行时常量池。
- 运行期间也可能将新的常量放入常量池,例如String.intern()方法。
6. 直接内存(Direct Memory)
简介
直接内存并不少虚拟机运行时内存的一部分,但这部分内存经常被使用,也可能出现OutOfMemoryError。
在JDK1.4新加入了NIO类,引入了一种基于Channel和Buffer的I/O方式。它可以使用Native函数直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景提供显著提供性能,避免在Java堆和Native堆来回复制数据。