1 Java虚拟机运行时数据区

2016-02-20 02:11:31 1,852

经常有人把Java内存区分为堆内存(Heap)和栈内存(Stack),这种区分方法比较粗糙,Java内存区域的划分实际上远比这复杂。Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,主要包括:

  • 虚拟机栈

  • 本地方法栈

  • PC寄存器

  • 方法区

  • 堆区

这些数据区域中大致可以划分为2类:线程独享、线程共享。

线程共享数据区:方法区、堆区。随着虚拟机启动而创建,随着虚拟机退出而销毁,并且为为进程的所有子线程共享 。

线程独享数据区:虚拟机栈、本地方法栈、PC寄存器。这些数据区与线程一一对应的,这些与线程对应的数据区域会随着线程开始和结束而创建和销毁。每个线程都有自己私有的虚拟机栈、本地方法栈、PC寄存器。

下图显示了两个线程的情况下,JVM中的运行时数据区:

Image.png

一、线程独享数据区

PC寄存器

Java 虚拟机可以支持多条线程同时执行,每一条 Java虚拟机线程都有自己的 PC( Program Counter)寄存器。在任意时刻,一条 Java 虚拟机线程只会执行一个方法的代码,这个正在被线程执行的方法称为该线程的当前方法( Current Method)。PC寄存器中存储的就是当前方法经过JVM汇编后字节码指令的地址。如果这个方法不是 native 的,那 PC 寄存器就保存 Java 虚拟机正在执行的字节码指令的地址,如果该方法是 native 的,那 PC 寄存器的值是 undefined。

Java虚拟机栈

此栈中的元素叫做栈帧(Stack Frame),线程在调用java方法时,会为每个方法创建一个栈帧,来存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用和完成的过程,都对应着一个栈帧从虚拟机栈上入栈和出栈的过程。虚拟机栈的生命周期和线程相同。

下图展示了栈帧的内部结构:

Image.png

栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接( Dynamic Linking)、方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。栈帧的存储空间分配在 Java 虚拟机栈之中,每一个栈帧都有自己的局部变量表( Local Variables)、操作数栈(OperandStack)和指向当前方法所属的类的运行时常量池的引用。局部变量表和操作数栈的容量是在编译期确定,并通过方法的 Code 属性保存及提供给栈帧使用。因此,栈帧容量的大小仅仅取决于 Java 虚拟机的实现和方法调用时可被分配的内存。在一条线程之中,只有目前正在执行的那个方法的栈帧是活动的。这个栈帧就被称为是当前栈帧( Current Frame),这个栈帧对应的方法就被称为是当前方法( Current Method),定义这个方法的类就称作当前类( Current Class)。对局部变量表和操作数栈的各种操作,通常都指的是对当前栈帧的对局部变量表和操作数栈进行的操作。法返回之后,当前栈帧就随之被丢弃,前一个栈帧就重新成为当前栈帧了。

请读者特别注意,栈帧是线程本地私有的数据,不可能在一个栈帧之中引用另外一条线程的栈帧。

本地方法栈

Java 虚拟机实现可能会使用到传统的栈(通常称之为“ C Stacks”)来支持 native 方法( 指使用 Java 以外的其他语言编写的方法)的执行,这个栈就是本地方法栈( Native Method Stack)。当 Java 虚拟机使用其他语言(例如 C 语言)来实现指令集解释器时,也会使用到本地方法栈。这个栈一般会在线程创建的时候按线程分配,用来存储线程调用本地方法时,本地方法的局部变量表,操作栈等信息。

Java 虚拟机规范允许Java虚拟机栈和本地方法栈被实现成固定大小的或者是根据计算动态扩展和收缩的。

本地方法栈和虚拟机栈所发挥的作用是非常相似的,区别是虚拟机栈执行Java方法,而本地方法栈则为虚拟机使用的Native方法服务,在sun hotspot 虚拟机中已经把两者合二为一了,本地方法栈区也会抛出StackOverFlowError和OutOfMemeoryError异常。

堆(Heap)

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。 此区域的唯一目的就是存放对象实例,几乎所有对象的实例都在这分配内存。 Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为GC堆。 Java 堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统( Automatic Storage Management System,也即是常说的“ Garbage Collector(垃圾收集器)”)所管理的各种对象,这些受管理的对象无需,也无法显式地被销毁。 Java 堆的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。

方法区

在 Java 虚拟机中,方法区( Method Area) 是可供各条线程共享的运行时内存区域。方法区与传统语言中的编译代码储存区( Storage Area Of Compiled Code)或者操作系统进程的正文段( Text Segment)的作用非常类似,它存储了每一个类的结构信息,例如运行时常量池( Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法。 方法区在虚拟机启动的时候被创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集。 方法区的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。

下图展示了方法区的内部结构

Image.png