JVM 内存区域

本文将介绍 JVM 中内存的各个区域。

JVM 中内存的各个区域主要如图所示:

一、程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

字节码解释器工作时就是通过改变程序计数器的值来选取需要执行的字节码指令,因此它也是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖它完成。

每个线程都有一个独立的程序计数器,各条线程之间的程序计数器互不影响、独立存储。

二、虚拟机栈

1. 什么是虚拟机栈?

虚拟机栈描述的是 Java 方法的执行。

虚拟机栈是线程私有的。

虚拟机栈的生命周期与线程相同。

2. 栈帧

栈帧是虚拟机栈的元素。每个方法被执行时,JVM 都会同步创建一个栈帧,方法被调用直至执行完毕的过程,就对应着栈帧在虚拟机栈中从入栈到出栈的过程。

栈帧存储的信息包括:

  • 局部变量表:用于存放方法参数及局部变量;其大小在编译期可以确认,会被提前计算好并放入方法的 Code 属性中的 max_locals 数据项中;可以存放数据类型为基本类型、对象引用、returnAddress 的数据

    returnAddress,即返回地址,它指向某个字节码指令。

    局部变量表中的 returnAddress 主要为 jsr、jsr_w 和 ret 指令服务,现在已经很少使用。

  • 操作数栈:主要作为方法调用的中转站,用于存放方法执行过程中产生的中间计算结果;在方法的执行过程中,各种指令会向栈中放入和提取数据;操作数栈的最大深度在编译期可以确认,会被提前计算好并放入方法的 Code 属性中的 max_stacks 数据项中;可以存放数据类型为基本类型、对象引用、returnAddress 的数据

  • 动态连接:一个指向运行时常量池中该栈帧对应方法的引用

  • 方法返回地址:记录方法的返回地址,用于在方法正常调用完成后,返回最初方法被调用时的位置

三、本地方法栈

本地方法栈与虚拟机栈十分相似,区别在于:虚拟机栈服务于 Java 方法的执行;本地方法栈服务于本地(Native)方法的执行。

四、堆

Java 中 “几乎” 所有的对象实例都在放置于堆中。

堆是所有线程共享的。

堆在虚拟机启动时创建。

五、方法区

方法区用于存储已被加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区是所有线程共享的。

方法区在虚拟机启动时创建。

虽然《Java 虚拟机规范》中把方法区描述为堆的一个逻辑部分,但它却有一个别名叫做 “非堆”,目的是与堆区分开来。

参考

  • 深入理解 Java 虚拟机