本文主要基于Java SE 8 版本的Java虚拟机规范和周志明老师的《深入理解Java虚拟机》。
[toc]
JVM Runtime Data Area
JVM定义了若干种程序运行期间会使用到的运行时数据区域Runtime Data Area,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁。
程序计数寄存器 The pc Register
每一条JVM线程都有自己的pc(Program Counter)寄存器。程序计数器记录当前线程正在执行的字节码指令命令地址(ReturnAddress类型),如果执行Native方法,这个计数值则为未定义的(Undefined)。程序计数寄存器是Java虚拟机规范中唯一规定不会出现任何异常情况的运行时数据区域。
Java虚拟机栈 Java Virtual Machine Stacks
- 虚拟机栈也是线程私有的,它的生命周期与线程相同。
- 虚拟机栈描述的是Java方法执行的内存模型。
- 每个方法在执行的同时都会创建一个栈帧。
- 栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 方法执行调用过程,就是栈帧在虚拟机栈中的入栈、出栈过程。
- 规范规定了这个区域的两种异常情况。
- 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
- 如果可以动态扩展,扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
堆 Heap
- Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。
- 唯一目的就是存在对象实例。
- 如果堆中沒有内存完成实例分配,并且堆也无法再扩展时,将会抛出OOM异常。
方法区 Method Area
- 方法区也是所有线程共享的一块内存区域。
- 它用于存储已被虚拟机加载的类信息、常量、静态常量、JIT编译后的代码等数据。
- 如果方法区沒有内存完成分配,将会抛出OOM异常。
PernGem和Metaspace
PernGem和Metaspace都是方法区的实现。
- PernGem (<1.8)
- 字符串常量位于PernGem
- FGC不会清理
- 大小启动的时候指定,不能变
- Metaspace (>=1.8)
- 字符串常量位于堆
- 会触发FGC清理
- 不设定的话,最大就是物理内存
为什么弃用PernGem
- 字符串存在永久代中,容易出现性能问题和内存溢出
- 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出
- 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
- 将 HotSpot 与 JRockit 合二为一
Run-Time Constant Pool 运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
本地方法栈 Native Method Stacks
- 存放native方法的栈帧,与底层硬件方法接触,不需要深究。
- 和虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。
直接内存/堆外内存
使用未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存。在配置虚拟机参数时,需要注意堆外内存的存在,以免各个内存区域总和大于物理内存限制,从而导致OOM异常。
栈帧 Frames
指令集架构有两种设计,一种是基于栈的指令集,另一种则是基于寄存器的指令集。前者是JVM采取的设计,后者是cpu采取的设计。
JVM以方法作为最基本的执行单元,栈帧则是用于支持jvm进行方法调用和方法执行背后的数据结构。栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。
局部变量表 Local Variables
用于存放方法参数和方法内部定义的局部变量的存储空间。在Java程序被编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量。
操作数栈 Operand Stacks
操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(Last In First Out,LIFO)栈。
动态链接 Dynamic Linking
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
方法调用正常完成 Normal Method Invocation Completion
执行到return指令时,方法调用正常完成。
方法调用异常完成 Abrupt Method Invocation Completion
执行到athrow指令或发生异常,且没捕获处理,方法调用异常完成。