JVM 运行时数据区域

简介

要去了解 Java 虚拟机,我们必须要先了解它的组成。那么 Java 虚拟机是如何定义运行时数据区域的呢?在运行时数据区域中在《Java 虚拟机规范(Java SE 7)》规定下主要为:
1. 程序计数器
2. Java 虚拟机栈
3. 本地方法栈
4. Java 堆
5. 方法区
6. 运行时常量池
7. 直接内存

程序计数器

程序计数器(Program Counter Register)是一块很小的内存,计算机科学中,计数器作为指令位置的一个行号指示器,取出当前的指令后,计数器会加一指向下一条指令。Java 的指令,分支,循环,跳转,异常处理,线程恢复等都依赖于程序计数器。多线程时,每个线程都会有各自独立的程序计数器。

当执行 Java 方法时,如果为一个 Native 修饰的方法,则计数器的值为空(Undefined)

Java 虚拟机栈

Java 虚拟机栈(Java Virtual Marchine Stacks)又被粗糙地称为栈内存是线程私有的,它的生命周期与线程相同。JVM 栈描述了 Java 方法执行的内存模型,每一次方法调用都会创建一个栈帧(Stack Frame)用于保存局部变量表、操作数栈、动态链接、方法出口等信息。方法执行与结束的过程对应着JVM 栈的入栈和出栈过程。

局部变量表存放了Java的基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型, 一个对象句柄或者对象指针又或者对象位置)、returnAddress 类型 (一条字节码的地址)。一个局部变量空间大致为32位。局部变量表的空间不会动态分配。

本地方法栈

本地方法栈(Native Method Stack)Java 虚拟机栈通常是一致的,不过为 Native Method 服务,在 Sun HotSpot VM 中,与Java 虚拟机栈合二为一

Java 堆

Java堆(Java Heap)是 JVM 管理的最大的,被所有线程共享的内存区域,用来保存大多数的对象实例,但这不是“绝对”的。Java 的垃圾回收集中在 Java 堆上,因此又被称为 GC 堆(Garbage Collected Heap)。

从内存回收的角度来看,由于现在收集器基本都采用分代回收算法,所以 Java 堆中分为:新生代和老年代。细致地可以分为:Eden 空间、From Survivor 空间、To Survivor 空间。
从内存分配角度来看,Java堆可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。

可以通过 -Xmx -Xms 来控制 Java 堆的大小。

方法区

方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,用于储存已经被虚拟机加载的类信息、常量、静态变量、即时翻译器编译后的代码等数据。为了区别方法区和堆通常,方法区有 Non-Heap 的别名。

通常不会对方法区进行内存回收因此又被称为(Java 堆的永久代),但这个区域是会被回收的。对这个区域的 GC 一般是常量池的回收和类型的卸载。

方法区空间的大小会受到 -XX:MaxPermSize 的影响。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分,Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号引用,这些存放在类加载后进入方法区的运行时常量池中存放。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,但也是一块重要的内存区域,避免了使用 NIO(New Input/Output)时,在Java 堆与Native 堆之间的数据交换。DirectByteBuffer 对象作为这块内存的引用。

了解完 JVM 运行时的内存区域,我们能够更加深入地去了解 JVM 中各区域的运行细节!