linux's docs

JVM: 1.0.0 运行时数据区域概念


1. 运行时数据区域概念概述

oracle 官方文档

JVM定义在程序执行期间使用的各种运行时数据区。 其中一些数据区域是在Java虚拟机启动时创建的,在JVM退出时才会被销毁。 其他数据区域是属于每个线程的,每个线程的数据区在创建线程时创建,并在线程退出时被销毁。

1) PC寄存器(program counter)

JVM可以同时支持许多线程在执行。 每个JVM线程都有自己的pc(程序计数器)寄存器。 在任何时候,每个JVM线程都会执行单个方法的代码,即该线程的当前方法。 如果该方法不是native的,则pc寄存器包含当前正在执行的JVM指令的地址。 如果线程当前正在执行的方法是native,那么JVM的pc寄存器的值是未定义的。Java虚拟机的pc寄存器足以在特定平台上保存returnAddress或本地指针。

2) JVM栈

每个JVM线程都有一个私有JVM栈,与线程同时创建。 一个JVM栈会存储frames。 Java虚拟机堆栈类似于诸如C的常规语言的堆栈:它保存局部变量和部分结果,并且在方法调用和返回中起作用。 因为Java虚拟机堆栈从来不被直接操作,除了push和pop frames,frames可以被堆分配。 JVM栈的内存不需要是连续的。

该规范允许JVM栈具有固定大小或者根据计算的需要动态地扩展和收缩。 如果JVM栈是固定大小的,则当创建该栈时,可以独立地选择每个JVM栈的大小。

JVM可以为程序员或用户提供对JVM栈初始大小的控制,以及在动态扩展或收缩Java虚拟机堆栈的情况下控制最大值和最小值。

以下异常与JVM栈相关:

  • 如果线程中的计算需要比所允许的JVM栈更大,则JVM将抛出StackOverflowError。
  • 如果JVM栈可以动态扩展,但尝试扩展时可用的内存不足,或者没有足够的内存可用于为新线程创建初始JVM栈,则JVM抛出OutOfMemoryError。

总结栈的特点

  • 每个线程有自己独立的JVM栈,也就是java方法的调用栈。同时JVM规范为了允许native代码可以调用Java代码,以及允许Java代码调用native方法,还规定每个Java线程拥有自己的独立的native方法栈。但这并不意味着每个java线程拥有两个栈,两个栈只是概念上区分,其实实际这两个方法调用栈是混在一起的。

3) JVM堆

在JVM中的堆(heap)被JVM线程所共享,堆是一个JVM的运行时数据区域,从中可以给类实例和数组分配内存。当JVM启动时创建堆内存区域,object的堆存储由自动存储管理系统(称为垃圾收集器)回收;object不会显式的释放内存。

以下异常条件与堆相关联:

  • 如果计算需要比自动存储管理系统可用的更多的堆,Java虚拟机将抛出OutOfMemoryError。

总结堆的特点

  • 堆在JVM启动时创建
  • 堆会被同一个java实例中的所有线程所共享
  • 由垃圾回收机制(GC:garbage collection)管理回收。

4) JVM方法区(永久代)

JVM具有在所有JVM线程之间共享的方法区。方法区类似于用于操作系统过程中的常规语言的编译代码或类似于“text”段的存储区域。它存储per-class结构,如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括在类和实例初始化和接口初始化中使用的特殊方法。

方法区在JVM启动时创建。虽然方法区在逻辑上是堆的一部分,但一般简单的情况下可能选择不对其进行垃圾回收或压缩它。本规范不强制用于管理编译代码的方法区域或策略的位置。方法区可以具有固定大小,或者可以根据计算的需要进行扩展,并且如果不需要更大的方法区,则可以收缩。方法区的内存不需要是连续的。

JVM实现可以为程序员或用户提供对方法区域的初始大小的控制,以及在变化大小的方法区的情况下,控制最大值和最小值。

以下异常条件与方法区域相关联:

  • 如果方法区域中的内存不能用于满足分配请求,则Java虚拟机将抛出OutOfMemoryError。

总结方法区的特点

  • JVM线程共享方法区
  • 方法区在JVM启动时创建

5) JVM常量池

常量池是为了避免频繁的创建和销毁对象而影响系统性能而分配的一个内存区域。运行时常量池是在一个class文件中的constant_pool表的每个类或每个接口的运行时表示

总结常量池特点

  • class文件中的常量池,class文件中保存了常量池信息,用于存放编译期生成的各种字面量(literal)和符号引用量(Symbolic References),这部分内容将在类加载后进入方法区的运行时常量池中存放
  • 方法区中的运行时常量池,与class文件常量池的区别在于,它具备动态性。
  • 常量池可节省内存(例如:会把相同的字符串常量合并),可节省运行时间(例如,比较字符串时,==只对比两个引用是否一致,不需要去比较内容)

6) JVM本地方法栈

JVM的实现可以使用俗称为“C栈”的常规栈来支持本地方法(以Java编程语言之外的语言编写的方法)。本地方法栈也可以通过在诸如C语言中实现用于JVM的指令集的解释器来使用。不能加载本地方法并且本身不依赖于常规栈的JVM实现不需要提供本地方法堆栈。如果提供,本地方法堆栈通常在每个线程创建时为每个线程分配。

该规范允许本地方法堆栈具有固定大小或者根据计算的需要动态地扩展和收缩。如果本地方法堆栈具有固定大小,则当创建该堆栈时,可以独立地选择每个本地方法堆栈的大小。 JVM实现可以为程序员或用户提供对本地方法栈的初始大小的控制,以及在变化大小的本地方法栈的情况下,控制最大和最小方法栈大小。

以下异常条件与本机方法堆栈相关联:

  • 如果线程中的计算需要比所允许的更大的本地方法堆栈,则JVM将抛出StackOverflowError。
  • 如果本地方法堆栈可以动态扩展并尝试本地方法堆栈扩展,但是内存不足可用,或者如果没有足够的内存可用于为新线程创建初始本地方法堆栈,Java虚拟机将抛出OutOfMemoryError 。