运行时数据区

Java虚拟机运行时将其管理的内存区域划分为几个数据区。包括以下几个:

  • 虚拟机栈(VM Stack)

    • 虚拟机栈也是线程私有的。生命周期与线程相同。
    • 虚拟机栈描述的是Java方法执行的线程内存模型:每个方法执行时,Java虚拟机会同步创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态链接方法出口等信息。一个方法从执行到结束,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
    • 对于C/C++中所谓的堆内存和栈内存,在Java中划分的要更为细腻一些。栈通常指的就是这里的虚拟机栈,或者更多情况下指的就是虚拟机栈的局部变量表部分。
    • 局部变量表存放了编译器克制的各种Java虚拟机基本数据结构(boolean, byte, char, short, int, long, float, double)、对象引用(reference类型,指向对象起始地址的指针,或者指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向一条字节码指令的地址)。
    • 上述数据结构在局部变量表中的存储空间以局部变量槽(slot)来表示的。其中64位的long和double型占两个槽,其余占1个。具体的每个槽占32bit还是64bit,取决于具体的虚拟机如何实现。
    • 《Java虚拟机规范》中,对此内存区域规定了两类异常情况:
      1. 线程请求栈深超过虚拟机所允许栈深,抛StackOverflowError异常。
      2. 若虚拟机栈容量可动态伸展,栈扩展是无法申请足够内存时,抛OutOfMemoryError异常。
  • 本地方法栈(Native Method Stack)

    该内存区域发挥的作用类似虚拟机栈,只是为本地方法提供服务。有的虚拟机(如HotSpot)直接把本地方法栈和虚拟机栈合为一体。抛异常也同虚拟机栈。

  • 程序计数器(Program Counter Register)

    • 是一块较小的内存空间 。是程序控制流的指示器。可以看做当前线程所执行java字节码的行号指示器。

    • 是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都要依赖这个计数器来完成。

    • 线程私有。

    • 若线程执行的是一个Java方法,PCR记录的是正在执行的虚拟机字节码指令的地址;如果是本地方法(Native),这个计数器的值应为空。

  • 堆区(Heap)

    • Java堆(Java Heap)是虚拟机所管理的内存中最大的一块儿。堆是所有线程共享的内存区域,在虚拟机启动时创建。此内存区域的唯一作用就是存放对象实例,java世界里几乎所有的对象实例都在这里分配内存。
    • Java堆是垃圾收集器管理的内存区域。GC操作的目标区域。
    • 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。堆即可被实现为固定的,也可被实现为可扩展的,不过当前主流虚拟机都将其实现为可扩展的。(通过参数 -Xms 和 -Xmx设定)。
    • 如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,抛OutOfMemoryError异常。
  • 方法区(Method Area)

    • 方法区同Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载类型信息常量静态变量即时编译器编译后的代码缓存等数据。《Java虚拟机规范》中把方法区描·述为堆的一个逻辑部分,但它却有一个别名“非堆”(Non-Heap),目的是与Java堆区分开来。
    • **static final Class 常量池 **放在方法区

方法区指的是理论概念。

持久代/元空间指的是实现机制。

JDK 1.6 及之前:有永久代,字符串常量池和运行时常量池都存在持久代。

JDK 1.7 : 有永久代,字符串常量池移到堆区 运行时常量池还在方法区。

JDK 1.8 及之后 :无永久代,字符串常量池在堆区,运行时常量池在元空间。