JVM虚拟机内存区域概述

未分类 发表评论


JVM虚拟机内存区域概述

虚拟机内存区域分配示意图

这里写图片描述

一、Java堆(Java Heap)

Java堆是Java虚拟机内存管理中占用空间最大的一块区域。Java堆是被所有线程共享的一片内存区域,在虚拟机的启动时而创建,被用来存储实例对象和数组。

Java堆是垃圾收集器(Garbage Collector)管理的主要区域。从内存回收的角度上来看,由于现在GC基本都采用分代收集算法,因此Java堆可被划分为新生代和老年代、Eden Space、From Survivor Space、To Survivor Space;从内存分配的角度来看,Java堆可被分为多个线程私有的分配缓冲区(Thread Local Allocation Buffer –> TLAB),这样做是为了线程安全

若Java堆存储实例对象过程中超出Java堆内存限制,则抛出OutOfMemoryError异常。

二、Java虚拟机栈(Stack)

Java虚拟机栈是线程私有的,它的生命周期与线程相同。栈是Java方法执行的内存模型。每个方法在执行的时候都会产生一个栈帧(Stack Frame)用于存储局部变量表等信息。广义上所指的“栈”就是Java虚拟机栈的局部变量表部分。其中,局部变量表存储了基本数据类型和引用数据类型的变量。Java虚拟机会在程序编译期间计算出该程序所需要的局部变量表的大小,因此,程序在运行时,Java虚拟机栈中的局部变量表的存储容量是固定的,不会改变。

举个例子:

public class JVMStacks{
    public static void main(String[] args){
        int a = 1;
        String b = new String("Hello World!");
    }
}

局部变量a为int这一基本数据类型,它被存储在Java虚拟机栈的局部变量表中。

局部变量b为String这一引用数据类型,它也被存储在Java虚拟机栈的局部变量表中。

new String(“Hello World!”)这一实例对象被存储在Java堆中。

new String(“Hello World!”)它的类型数据被存储在方法区中。

大家可能会有疑惑,局部变量b是如何引用(也可称指向)new String(“Hello World!);这一实例对象的呢?

过程是这样的:

String b = new String(“Hello World!”);

Java虚拟机收到new String(“Hello World!”);这一实例对象的请求,于是Java虚拟机在Java堆中申请了一块与该对象大小所对应的内存空间(有可能进行Thread Local Allocation Buffer这一过程),随后创建这一对象,这块内存空间就一个独一无二的内存地址值(好比身份证),代表该对象。随后,在方法区中注册该实例对象,把该实例对象的类型数据存储在方法区中,并与其实例对象建立索引。

创建一个String类型的实例对象,定义一个String类型的引用数据变量b。将实例对象赋值给b这一引用变量,至此,b就存储了指向new String(“Hello World!”);这一对象的指针,该指针指向该对象的内存地址值,该对象的内存地址值也对应了该对象在方法区的类型数据。至此,局部变量b就完全拥有了该实例对象的引用,并可以对该实例的变量,方法进行操纵。

这里写图片描述

这就是直接指针访问这一方式。

当然,还有一种引用实例对象的方式:句柄

Java虚拟机会在堆内存中分配一块很小的内存空间,这个内存空间叫做句柄池。句柄池存储了指向实例对象和实例对象所对应的位于方法区的类型数据(类信息、常量、静态变量、静态方法)的指针(Pointer)。

这里写图片描述

但在运行时,如果线程请求的栈深度超出了虚拟机所允许的深度,就会抛出StackOverflowError异常。目前,大多数Java虚拟机都支持动态扩展内存(运行期间扩展内存)。如果Java虚拟机栈不能扩展内存时,将会抛出OutOfMemoryError异常。

三、方法区(Method Area)

方法区是各线程共享的内存区域,这点与Java堆内存相似。方法区存储了Java堆中实例对象的类信息、常量、静态变量、静态方法、即时编译器编译后的代码等数据。从广义上讲,方法区被称为永久代(Permanent Generation)。垃圾收集行为很少在方法区出现。

当Java虚拟机在方法区的内存分配策略无法满足方法区实际存储量时,会抛出OutOfMemoryError异常。

四、本地方法栈(Native Method Stack)

本地方法栈的功能与Java虚拟机栈的功能很类似。Java虚拟机栈提供虚拟机运行Java方法时的内存空间;本地方法栈提供虚拟机运行带有Native关键字的方法时的内存空间。

同Java虚拟机栈一样,会抛出StackOverflowError、OutOfMemoryError异常。

五、程序计数器(Program Counter Register)

程序计数器是当前线程执行字节码文件过程中的行号解释器(Line Number Interpretor)。程序计数器是线程私有的。每个线程对应一个独立的程序计数器。Java虚拟机中的字节码解释器,我们暂且称java.exe(当然,javac.exe是编译器)。java.exe在运行过程中需要不断调整计数器的值来选择下一条执行字节码的指令。分支、跳转、循环、异常处理、线程恢复等基础功能都依赖于程序计数器。

程序计数器为虚拟机执行Java方法服务,不为虚拟机执行Native方法服务。

发表评论

邮箱地址不会被公开。 必填项已用*标注

昵称 *