Java 虚拟机 - 运行时数据区域
这篇描述了运行时各个区域的作用,以便后续进一步讲解 Java 虚拟机相关内容。
简介
要去了解 Java 虚拟机,我们必须要先了解它的组成。那么 Java 虚拟机是如何定义运行时数据区域的呢?在运行时数据区域中在《Java 虚拟机规范(Java SE 7)》规定下主要为:
- 程序计数器
- Java 虚拟机栈
- 本地方法栈
- Java 堆
- 方法区
- 运行时常量池
- 直接内存
程序计数器
程序计数器(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 中各区域的运行细节!