乐趣区

关于java:我所知道JVM虚拟机之运行时数据区的概述与程序计数器PC寄存器

前言


本篇开始学习运行时数据区,上篇文章对类加载子系统做了解说,后续重点开展观看字节码指令去理解

当初咱们晓得类加载器外面有什么,做了什么事件就能够

其实当咱们类加载器把类寄存到内存当中办法区实际上曾经开始应用运行时数据区了,接下来就须要对讲讲运行时数据区进行解说了

一、运行时数据区概述


后面咱们说过咱们的类会通过类加载器进行的加载 –> 验证 –> 筹备 –> 解析 –> 初始化,这几个阶段实现后就会用到执行引擎对咱们的类进行应用,同时执行引擎将会应用到咱们运行时数据区

咱们能够类比一下也就是大厨做饭,咱们把大厨前面的货色(切好的菜,刀,调料),比作是运行时数据区。而厨师能够类比于执行引擎,将通过筹备的货色进行制作成精美的菜品

运行时数据区其实咱们用的就是内存了,而内存是十分重要的系统资源,是硬盘和 CPU 的两头仓库及桥梁,承载着操作系统和应用程序的实时运行

咱们通过磁盘或者网络 IO 失去的数据,都须要先加载到内存中,而后 CPU 从内存中获取数据进行读取

JVM 内存布局规定了 Java 在运行过程中内存申请、调配、治理的策略,保障了 JVM 的高效稳固运行,以及不同的 JVM 对于内存的划分形式和管理机制存在着局部差别

咱们能够看看阿里巴巴手册 JDK8 的内存图

对于线程的内存

================================
Java 虚拟机定义了若干种程序运行期间会应用到的运行时数据区:

  • 其中有一些会随着虚拟机启动而创立,随着虚拟机退出而销毁。
  • 另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程开始和完结而创立和销毁

咱们这里说灰色的为独自线程公有的,红色的为多个线程共享的即

  • 每个线程独有:独立包含程序计数器、栈、本地办法栈
  • 而线程共享有:堆、堆外内存(永恒代或元空间、代码缓存)

如果咱们当初有五个线程,则会有五组灰色区域,而共用一个红色区域

对于 Runtime 类

================================
每个 JVM 只有一个 Runtime 实例。即为运行时环境,相当于内存构造的两头的那个框框:运行时环境

二、从虚拟机看线程


咱们说线程是一个程序里的运行单元而在 JVM 中容许一个利用有多个线程并行的执行

在 Hotspot JVM 里,每个线程都与操作系统的本地线程间接映射

当一个 Java 线程筹备好执行当前,此时一个操作系统的本地线程也同时创立。Java 线程执行终止后,本地线程也会回收

操作系统负责将线程安顿调度到任何一个可用的 CPU 上。一旦本地线程初始化胜利,它就会调用 Java 线程中的 run() 办法

JVM 零碎线程

================================
咱们能够应用 jconsole 或者是任何一个调试工具,都能看到在后盾有许多线程在运行

不过这些线程不包含调用 public static void main(String[]) 的 main 线程以及所有这个 main 线程本人创立的线程

而这些后盾零碎线程在 Hotspot JVM 次要有以下几种:

  • 虚拟机线程 :这种线程的操作是须要 JVM 达到平安点才会呈现。这些操作必须在不同的线程中产生的起因是他们都须要 JVM 达到平安点,这样堆才不会变动。这种线程的执行类型括”stop-the-world”的垃圾收集,线程栈收集,线程挂起以及偏差锁撤销
  • 周期工作线程 :这种线程是工夫周期事件的体现(比方中断),他们个别用于周期性操作的调度执行
  • GC 线程 :这种线程对在 JVM 里不同品种的垃圾收集行为提供了反对
  • 编译线程 :这种线程在运行时会将字节码编译成到本地代码
  • 信号调度线程 :这种线程接管信号并发送给 JVM,在它外部通过调用适当的办法进行解决

三、运行时数据区之程序计数器(PC 寄存器)


首推官网的 PC 寄存器介绍:拜访地址入口

JVM 中的程序计数寄存器(Program Counter Register)中,Register 的命名源于 CPU 的寄存器,寄存器存储指令相干的现场信息。CPU 只有把数据装载到寄存器才可能运行

JVM 中的 PC 寄存器是对物理 PC 寄存器的一种形象模仿

PC 寄存器作用

================================
PC 寄存器用来存储指向下一条指令的地址,也行将要执行的指令代码。由执行引擎读取下一条指令,并执行该指令。

PC 寄存器介绍

================================
它是一块很小的内存空间,简直能够疏忽不记。也是运行速度最快的存储区域

在 JVM 标准中,每个线程都有它本人的程序计数器,是线程公有的,生命周期与线程的生命周期保持一致

任何工夫一个线程都只有一个办法在执行,也就是所谓的以后办法。

程序计数器会存储以后线程正在执行的 Java 办法的 JVM 指令地址;或者如果是在执行 native(本地办法栈)办法,则是未指定值(undefined)

它是程序控制流的指示器,分支、循环、跳转、异样解决、线程复原等根底性能都须要依赖这个计数器来实现

字节码解释器工作时就是通过扭转这个计数器的值来选取下一条须要执行的字节码指令

它是惟一一个在 Java 虚拟机标准中没有规定任何 OutofMemoryError 状况的区域

举例领会 PC 寄存器

public class PCRegisterTest {public static void main(String[] args) {
        
        int i = 10;
        int j = 20;
        int k = i + j;

        String s = "abc";
        System.out.println(i);
        System.out.println(k);

    }
}

咱们先通过字节码来看看信息

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=5, args_size=1
         0: bipush        10
         2: istore_1
         3: bipush        20
         5: istore_2
         6: iload_1
         7: iload_2
         8: iadd
         9: istore_3
        10: ldc           #2                  // String abc
        12: astore        4
        14: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        17: iload_1
        18: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        21: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        24: iload_3
        25: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        28: return
      LineNumberTable:
        line 10: 0
        line 11: 3
        line 12: 6
        line 14: 10
        line 15: 14
        line 16: 21
        line 18: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  args   [Ljava/lang/String;
            3      26     1     i   I
            6      23     2     j   I
           10      19     3     k   I
           14      15     4     s   Ljava/lang/String;

右边的数字代表指令地址(指令偏移),即 PC 寄存器中可能存储的值,而后执行引擎读取 PC 寄存器中的值,并执行该指令

比如说 PC 寄存器寄存指令地址:5,而执行引擎会看到 PC 寄存器寄存的是 5,那么会去对应的操作指令:instore_2,将它放到操作局部变量表、操作数栈保留,并且讲字节码指令翻译成机器指令让 CPU 执行

常见面试题

================================

  • 应用 PC 寄存器存储字节码指令地址有什么用呢?
  • 为什么应用 PC 寄存器来记录以后线程的执行地址呢?

因为 CPU 须要不停的切换各个线程,这时候切换回来当前,就得晓得接着从哪开始继续执行

JVM 的字节码解释器就须要通过扭转 PC 寄存器的值来明确下一条应该执行什么样的字节码指令

  • PC 寄存器为什么被设定为公有的?

因为咱们晓得所谓的多线程是在一个特定的时间段内只会执行其中某一个线程的办法

而 CPU 会不停地做工作切换,这样必然导致常常中断或复原,如何保障分毫无差呢?

最好的方法天然是为每一个线程都调配一个 PC 寄存器,精确地记录各个线程正在执行的以后字节码指令地址。这样一来各个线程之间便能够进行独立计算,从而不会呈现互相烦扰的状况

CPU 工夫片

================================
CPU 工夫片即 CPU 调配给各个程序的工夫,每个线程被调配一个时间段,称作它的工夫片

在宏观上:咱们能够同时关上多个应用程序,每个程序并行不悖,同时运行

但在宏观上:因为只有一个 CPU,一次只能处理程序要求的一部分,如何解决偏心,一种办法就是引入工夫片,每个程序轮流执行

并发和并行的区别

================================
这里咱们解说一下并发与并行的区别

并行(Parallel):当零碎有一个以上 CPU 时,当一个 CPU 执行一个过程时,另一个 CPU 能够执行另一个过程,两个过程互不抢占 CPU 资源,能够同时进行,这种形式咱们称之为并行

并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行结束之间,且这几个程序都是在同一个处理机上运行

用网吧的机子来说咱们有十台电脑,早晨能够让十个人同时上机,这叫并行同时进行,互不争抢

如果咱们只有一台电脑然而有五个人来上机,可能就是每个人玩一段时间或者罗唆期待他人下机再到你

参考资料


尚硅谷:JVM 虚拟机(宋红康老师)

退出移动版