02-JVM之运行时数据区

2022-07-18

02-JVM之运行时数据区

JVM之运行时数据区(一)

一. 前言

运行时数据区主要包括程序计数器本地方法栈方法区虚拟机栈

当我们通过前面的:类的加载 --> 验证 --> 准备 --> 解析 --> 初始化,这几个阶段完成后,就会用到执行引擎对我们的类进行使用,同时执行引擎将会使用到我们运行时数据区

对于一个线程来说,有些内存区域是独有的,有些内存区域是共享的

  • 线程独有:程序计数器、栈、本地方法栈
  • 线程间共享:堆、堆外内存(永久代或元空间、代码缓存)

JVM系统线程

  1. 虚拟机线程:这种线程的操作是需要JVM达到安全点才会出现。这些操作必须在不同的线程中发生的原因是他们都需要JVM达到安全点,这样堆才不会变化。这种线程的执行类型括"stop-the-world"的垃圾收集,线程栈收集,线程挂起以及偏向锁撤销
  2. 周期任务线程:这种线程是时间周期事件的体现(比如中断),他们一般用于周期性操作的调度执行
  3. GC线程:这种线程对在JVM里不同种类的垃圾收集行为提供了支持
  4. 编译线程:这种线程在运行时会将字节码编译成到本地代码
  5. 信号调度线程:这种线程接收信号并发送给JVM,在它内部通过调用适当的方法进行处理

二. 程序计数器

又称为PC寄存器

2.1 介绍

  1. JVM中的程序计数寄存器(Program Counter Register)中,Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。
  2. 这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟
  3. 它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域。
  4. 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。
  5. 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行native方法,则是未指定值(undefned)。
  6. 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
  7. 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
  8. 它是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。

2.2 作用

程序计数器是用来存储下一条需要执行的指令。由执行引擎读取下一条指令,并执行该指令

由于cpu会不断的在多个线程之间进行切换,当执行一个线程的时候,其他的线程就会处于停止状态。只不过由于cpu切换速度很快,我们感觉上是同时执行的。

因此,程序计数器需要设计为每个线程一份,用来保存自己线程需要执行的下一条指令,这样cpu在切换过来后可以准确的继续执行

小案例

public class PcRegisterTest {

    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = a + b;
    }
}

观察如上代码产生的字节码文件

image-20210624223430204

执行引擎就是根据程序计数器中存储的指令地址,将所在指令翻译为机器代码,由cpu去执行

三. 本地方法接口

  1. 简单地讲,一个Native Method是一个Java调用非Java代码的接囗一个Native Method是这样一个Java方法:该方法的实现由非Java语言实现,比如C。这个特征并非Java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern 告知C++编译器去调用一个C的函数。
  2. “A native method is a Java method whose implementation is provided by non-java code.”(本地方法是一个非Java的方法,它的具体实现是非Java代码的实现)
  3. 在定义一个native method时,并不提供实现体(有些像定义一个Java interface),因为其实现体是由非java语言在外面实现的。
  4. 本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序。

四. 本地方法栈

  1. Java虚拟机栈于管理Java方法的调用,而本地方法栈用于管理本地方法的调用
  2. 本地方法栈,也是线程私有的。
  3. 允许被实现成固定或者是可动态扩展的内存大小(在内存溢出方面和虚拟机栈相同)
    • 如果线程请求分配的栈容量超过本地方法栈允许的最大容量,Java虚拟机将会抛出一个stackoverflowError 异常。
    • 如果本地方法栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的本地方法栈,那么Java虚拟机将会抛出一个outofMemoryError异常。
  4. 本地方法一般是使用C语言或C++语言实现的。
  5. 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

注意事项

  1. 当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。它和虚拟机拥有同样的权限。
    • 本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区
    • 它甚至可以直接使用本地处理器中的寄存器
    • 直接从本地内存的堆中分配任意数量的内存
  2. 并不是所有的JVM都支持本地方法。因为Java虚拟机规范并没有明确要求本地方法栈的使用语言、具体实现方式、数据结构等。如果JVM产品不打算支持native方法,也可以无需实现本地方法栈。
  3. 在Hotspot JVM中,直接将本地方法栈和虚拟机栈合二为一。

标题:02-JVM之运行时数据区
作者:mahaonan
地址:https://mahaonan.fun/articles/2022/07/18/1658147067214.html