1 运行时数据区域

jvm 1.7

1.1 程序计数器(Program Counter Register)

  • 线程私有的。
  • 当前线程所执行字节码的行号指示器,字节码解释器的作用是通过改变计数器的值来选取下一条需要执行的字节码指令。
  • 如果执行的方法不是native的,计数器包含当前正在执行的Java虚拟机指令的地址。
  • 如果当前执行的是native方法,则这个计数器为undefined
  • Java虚拟机中唯一没有OOM的区域。

1.2 Java虚拟机栈 (Java Virtual Machine Stacks)

  • 线程私有的。
  • 虚拟机栈描述的是Java方法执行的内存模型,生命周期与线程相同。
  • 每个方法运行都会创建一个栈帧,存储局部变量表(Local Variable Table),局部结果集等信息。
  • 在编译期分配局部变量表存放各种基本数据类型、对象引用类型和returnAddress(指向一条字节码指令的地址)。
  • 如果请求的栈深度大于最大可用栈深度时,系统就会抛出StackOverflowError错误。
  • 如果虚拟机动态扩展无法申请到足够的的内存时会抛出OOM异常。大部分虚拟机都可以动态扩展,当然也允许固定长度虚拟机栈。

returnAddress类型的值是指向Java虚拟机指令的操作码的指针。与数字基本类型不同,returnAddress类型不对应任何Java编程语言类型,并且不能由正在运行的程序修改。

1.3 本地方法栈(Native Method Stack)

  • 线程私有的。
  • 虚拟机栈为执行Java方法服务,本地方法栈为Native方法服务。
  • 会出现OOM或者StackOverflowError,原因和虚拟机栈类似。

1.4 Java堆(Heap)

  • 虚拟机启动时创建被所有线程共享的内存区域,为了存储所有对象实例和数组。垃圾回收的主要区域。

1.5 方法区(Method Area)

  • 别名:“永久代“、”非堆,各个线程共享的内存区域,存储虚拟机已加载的类信息,静态变量、常量、即时编译器编译后的代码数据。默认最小为16MB,最大为64MB,可以通过-XX:PermSize和-XX:MaxPermSize 参数限制方法区大小。
  • Java8中,已经彻底没有了永久代,将方法区直接放在一个与堆不相连的本地内存区域,这个区域被叫做元空间。

1.6 运行时常量池(Runtime Constant Pool)

  • 运行时常量池:jdk1.6及之前是方法区的一部分,其中的主要内容来自于JVM对Class的加载。Java7中已经将运行时常量池从方法区(永久代)移除,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。
  • 类加载后存储编译器生成的常量(各种字面量和符号引用)。符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:类和接口的全限定名、字段名称和描述符、方法名称和描述符
  • 由于运行时常量池从方法区移动到堆,所以jdk1.6和jdk1.7及之后的版本对于常量池的使用还是有些区别的,深入解析String#intern
  • 运行时常量池是类文件中constant_pool表的每类或每接口运行时表示。它包含几种常量,从编译时已知的数字值到必须在运行时解析的方法和字段引用。运行时常量池提供类似于传统编程语言的符号表的功能,尽管它包含比典型符号表更宽范围的数据。

1.7 直接内存(Direct Memory)

  • 堆外内存,JVM虚拟机各个区域内存总和大于机器物理内存可能会导致OOM。

参考资料

深入理解Java虚拟机-JVM高级特性与最佳实践
Chapter 2. The Structure of the Java Virtual Machine
JVM常量池浅析