JVM内存划分/运行时数据区
JVM内存结构图

  1. 程序计数器(Program Counter Register)
    当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。

  2. 虚拟机栈(Vm Stack)
    描述Java方法执行的内存模型。每个方法执行的时候都会创建一个栈帧(Stack Frame),栈帧用于存储局部变量表(包括参数)、操作数栈、动态链接、方法出口等信息。每个方法从调用开始到结束的过程,都对应着Vm Stack中的入栈到出栈的过程。在单线程情况下错误肯定是StackOverFlowError,多线程下OutOfMemoryError。

虚拟机栈图

// age变量和name引用都是存储在虚拟机栈的栈帧
public void test() {
    String name = "stormma";
    int age = 21;
}

简单介绍局部变量表,操作数栈,动态链接,方法出口概念。

  • 局部变量表:局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,其中存放的数据的类型是编译期可知的各种基本数据类型、对象引用(reference)和(returnAddress)类型(它指向一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配的,即在Java程序被编译成Class文件时,就确定了所需分配的最大局部变量表的容量。当进入一个方法时,这个方法需要在栈中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
  • 操作数栈:操作数栈的最大深度也是在编译的时确定的。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。当一个方法开始执行时,它的操作数栈是空的,在方法的执行过程中,会有各种字节码指令(比如:加操作、赋值元算等)向操作数栈中写入和读取内容,也就是入栈和出栈操作。Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所指的“栈”就是操作数栈。(Android虚拟机基于寄存器)
  • 动态链接:每个栈帧都包含一个指向运行时常量池(在方法区中,后面介绍)中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如final、static 域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。
  • 方法返回地址:当一个方法被执行后,有两种方式退出该方法,执行引擎遇到任意一个方法返回的字节码指令或遇到异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作栈,如果有返回值,则把它压入调用者栈帧的操作栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。
  1. 堆(Heap)
    Java虚拟机所管理的内存中最大的一块内存区域,被各个线程共享,在JVM启动时创建。该内存区域存放对象实例及数组(所有new的对象)。其大小通过-Xms(最小值)和-Xmx(最大值)参数设置,-Xms为JVM启动时申请的最小内存,-Xmx为JVM可申请的最大内存。在JVM启动时,最大内存会被保留下来。为对象内存而保留的地址空间可以被分成年轻代和老年代。

  2. 本地方法栈(Native Method Stack)
    其与虚拟机栈的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native(Java Native关键字)方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

  3. 方法区(Method-Area)
    方法区主要用于存储虚拟机加载的类信息、final常量、静态变量,以及编译器编译后的代码等数据。在jdk8中,Hotspot去掉永久代这一说法。原方法区中存储的类信息、编译后的代码数据等已经移动到了元空间(MetaSpace)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。元空间的大小仅受本地内存限制,但可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize来指定元空间的大小。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐