JVM内存模型是理解JVM虚拟机的重要基础之一。下面我们将通过剖析JVM内存模型的每个模块,来深入理解JVM内存模型。
JVM内存模型如下图:
其中程序计数器、虚拟机栈、本地方法栈这些是线程隔离的;堆、方法区是线程共享的。
1. 程序计数器
程序计数器是较小的内存空间,可以把它理解为当前线程需要执行的字节码的显示器。它控制着分支、循环、跳转、异常处理、线程恢复等基础功能。
Java多线程是通过线程轮流切换、分配CPU执行时间片的方式来实现的,在确定的一个时刻,一个CPU内核都只会执行一条线程中的指令。因此,
每条线程都有一个独立的程序计数器,确保各个线程的指令执行互不影响。
2. Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程保持一致。
当每个方法被执行的时候,Java虚拟机都会创建一个栈帧(Stack Frame)来存储局部变量表、操作数栈、动态连接、方法出口等信息。每个方法被调用直至执行完毕的过程,就对应了一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表保存编译期间可以知道的各种Java基本数据类型(boolean, btye, char, short, int , float, long, double)、对象引用和ReturnAddress类型。它们以局部变量槽(Slot)来表示,其中long 和 double占两个,其它类型占一个。
3. 本地方法栈
本地方法栈与虚拟机栈所发挥的作用是相似的,区别在于虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈是为了虚拟机使用本地(Native)方法服务。
4. Java堆
Java堆(Java Heap) 是虚拟机所管理内存中最大的一块。它是Java所有线程共享的一块内存区域,在虚拟机启动时候创建。它的唯一目的是存放对象实例,Java中几乎所有的对象实例都在这里分配内存。
Java堆是垃圾回收器管理的内存区域,GC Heap就是我们常说的GC堆。从回收内存的角度,由于现代垃圾收集器大部分都是基于分代收理论设计的,所以我们常会看到“新生代”、“老生代”、“永久代”、“Eden空间”、“From Survivor空间”、“To Survivor空间”。
Java堆一般通过参数-Xmx和-Xms设定。
5. 方法区
方法区与堆一样是各个线程共享的内存区域,用来存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。它的别称是Non-Heap。
运行时常量池是方法区的一部分,来保存编译期生成的各种字面量与符号引用。
6. 直接内存
直接内存不是虚拟机运行时的一部分,但它也会被频繁使用。比如NIO,引入了一种基于通道(Channel)与缓存区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆以外的内存,然后通过堆里面的
DirectByteBuffer对象作为这块内存的引用来操作。可以大大提高性能,避免了Java堆和Native堆来回复制数据。
部分内容参考了周志明的《深入理解Java虚拟机栈》。