JVM内存模型

203 阅读4分钟

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

image.png

1.程序计数器

  • 每个线程都有一个程序计数器,线程私有,是一块较小的内存空间,就是一个指针指向方法区中的方法字节码, 是当前线程所执行的字节码的行号指示器。
  • 正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址) 。
  • 如果还是 Native 方法,则为空。 这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域

主要有两个作用:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制,如:顺序执 ⾏、选择、循环、异常处理。

  2. 在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时 候能够知道该线程上次运⾏到哪⼉了。

2.虚拟机栈(Java栈)

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

3.本地方法栈

本地方法区和虚拟机栈作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为 Native 方法服务, HotSpot虚拟机直接就把本地方法栈和虚拟机栈合二为一。

4.堆-运行时数据区

堆是被线程共享的一块内存区域, 创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行 垃圾收集的最重要的内存区域。 由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以 细分为: 新生代(Eden 区、 From Survivor 区和 To Survivor 区)和老年代。

5.方法区

即我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、 常量、 静 态变量、 即时编译器编译后的代码等数据. HotSpot VM把GC分代收集扩展至方法区, 即使用Java 堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。 运行时常量池(Runtime Constant Pool)是方法区的一部分。 Class 文件中除了有类的版 本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。

方法区和永久代的关系: 永久代是HotSpot虚拟机规范中对于方法区的一种实现,也就是说永久代是HotSpot的概念,方法区是一种规范,而永久代是HotSpot虚拟机中对于这种规范的实现,其他虚拟机中没有这个概念。

元空间

在 Java8 中, 永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间 的本质和永久代类似,元空间与永久代最大的区别在于:

  • 元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

  • 类的元数据放入本地内存, 字符串池和类的静态变量放入 java 堆中, 这样可以加载多少类的元数据就不再由MaxPermSize 控制, 而由系统的实际可用空间来控制。能够加载更多的类,虽然元空间可能溢出但是出现的几率比原来小。