Java运行时数据区

image.pngimage.png
线程私有:

  1. 程序计数器:一块较小的内存空间,当前线程所执行的字节码(.class)的行号指示器字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,实现代码的流程控制。为了线程切换后能恢复,因此每个线程的程序计数器要独立

⚠️ 注意 :程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

  1. 虚拟机栈:它的生命周期和线程相同。除了一些 Native 方法调用是通过本地方法栈实现的(后面会提到),其他所有的 Java 方法调用都是通过栈来实现的(需要和其他运行时数据区域比如程序计数器配合)

JVM虚拟机 - 图3
程序运行中栈可能会出现两种错误:

  • StackOverFlowError: 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
  • OutOfMemoryError: 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

HotSpot虚拟机的栈容量是不可以动态扩展的,所以HotSpot虚拟机不会由于虚拟机栈无法扩展而导致OOM异常,但是如果申请时就失败,还是会发生OOM异常。

  1. 本地方法栈:和虚拟机栈类似,虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

也会随着本地方法的生命周期将栈帧压栈、出栈(并释放内存空间),也会出StackOverFlowError 和 OutOfMemoryError 两种错误。

线程共享:

  1. 堆:内存最大的一块,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆。
JVM虚拟机 - 图4堆这里最容易出现的就是 OutOfMemoryError 错误,并且出现这种错误之后的表现形式还会有几种,比如:

  1. java.lang.OutOfMemoryError: GC Overhead Limit Exceeded : 当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
  2. java.lang.OutOfMemoryError: Java heap space :假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。

    1. 方法区:

方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

方法区和永久代以及元空间的关系:JVM虚拟机 - 图5

运行时常量池 是方法区的一部分,用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域
JVM虚拟机 - 图6

HotSpot 虚拟机在 Java 堆中对象分配、布局和访问的全过程