• 【JVM】程序计数器(四)


    一、PC 寄存器概述

      PC 寄存器介绍  

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

    2. 这里,并非是广义上所指的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟

    3. 它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域

    4. 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致

    5. 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行native方法,则是未指定值(undefned)。

    6. 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

    7. 它是唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域。

      

      PC 寄存器的作用

      PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎将指令解释为机器码交由cpu进行处理

      

    二、代码示例

    • 代码
       1 public class PCRegisterTest {
       2 
       3     public static void main(String[] args) {
       4         int i = 10;
       5         int j = 20;
       6         int k = i + j;
       7 
       8         String s = "abc";
       9 
      10         System.out.println(k);
      11         System.out.println(s);
      12     }
      13 }  
    • 反编译:javap -v xxx.class
        1 MacBook-Pro-22907:jvm h__d$ pwd
        2 /Users/h__d/Documents/workspace-idea/test-jvm/out/production/day04/com/test/jvm
        3 MacBook-Pro-22907:jvm h__d$ javap -v PCRegisterTest.class 
        4 Classfile /Users/h__d/Documents/workspace-idea/test-jvm/out/production/day04/com/test/jvm/PCRegisterTest.class
        5   Last modified 2020-12-11; size 702 bytes
        6   MD5 checksum c27960ecaf15720393724a60083d4e13
        7   Compiled from "PCRegisterTest.java"
        8 public class com.test.jvm.PCRegisterTest
        9   minor version: 0
       10   major version: 52
       11   flags: ACC_PUBLIC, ACC_SUPER
       12 Constant pool:
       13    #1 = Methodref          #7.#27         // java/lang/Object."<init>":()V
       14    #2 = String             #28            // abc
       15    #3 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
       16    #4 = Methodref          #31.#32        // java/io/PrintStream.println:(I)V
       17    #5 = Methodref          #31.#33        // java/io/PrintStream.println:(Ljava/lang/String;)V
       18    #6 = Class              #34            // com/test/jvm/PCRegisterTest
       19    #7 = Class              #35            // java/lang/Object
       20    #8 = Utf8               <init>
       21    #9 = Utf8               ()V
       22   #10 = Utf8               Code
       23   #11 = Utf8               LineNumberTable
       24   #12 = Utf8               LocalVariableTable
       25   #13 = Utf8               this
       26   #14 = Utf8               Lcom/test/jvm/PCRegisterTest;
       27   #15 = Utf8               main
       28   #16 = Utf8               ([Ljava/lang/String;)V
       29   #17 = Utf8               args
       30   #18 = Utf8               [Ljava/lang/String;
       31   #19 = Utf8               i
       32   #20 = Utf8               I
       33   #21 = Utf8               j
       34   #22 = Utf8               k
       35   #23 = Utf8               s
       36   #24 = Utf8               Ljava/lang/String;
       37   #25 = Utf8               SourceFile
       38   #26 = Utf8               PCRegisterTest.java
       39   #27 = NameAndType        #8:#9          // "<init>":()V
       40   #28 = Utf8               abc
       41   #29 = Class              #36            // java/lang/System
       42   #30 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
       43   #31 = Class              #39            // java/io/PrintStream
       44   #32 = NameAndType        #40:#41        // println:(I)V
       45   #33 = NameAndType        #40:#42        // println:(Ljava/lang/String;)V
       46   #34 = Utf8               com/test/jvm/PCRegisterTest
       47   #35 = Utf8               java/lang/Object
       48   #36 = Utf8               java/lang/System
       49   #37 = Utf8               out
       50   #38 = Utf8               Ljava/io/PrintStream;
       51   #39 = Utf8               java/io/PrintStream
       52   #40 = Utf8               println
       53   #41 = Utf8               (I)V
       54   #42 = Utf8               (Ljava/lang/String;)V
       55 {
       56   public com.test.jvm.PCRegisterTest();
       57     descriptor: ()V
       58     flags: ACC_PUBLIC
       59     Code:
       60       stack=1, locals=1, args_size=1
       61          0: aload_0
       62          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       63          4: return
       64       LineNumberTable:
       65         line 3: 0
       66       LocalVariableTable:
       67         Start  Length  Slot  Name   Signature
       68             0       5     0  this   Lcom/test/jvm/PCRegisterTest;
       69 
       70   public static void main(java.lang.String[]);
       71     descriptor: ([Ljava/lang/String;)V
       72     flags: ACC_PUBLIC, ACC_STATIC
       73     Code:
       74       stack=2, locals=5, args_size=1
       75          0: bipush        10
       76          2: istore_1
       77          3: bipush        20
       78          5: istore_2
       79          6: iload_1
       80          7: iload_2
       81          8: iadd
       82          9: istore_3
       83         10: ldc           #2                  // String abc
       84         12: astore        4
       85         14: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       86         17: iload_3
       87         18: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
       88         21: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       89         24: aload         4
       90         26: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       91         29: return
       92       LineNumberTable:
       93         line 6: 0
       94         line 7: 3
       95         line 8: 6
       96         line 10: 10
       97         line 12: 14
       98         line 13: 21
       99         line 14: 29
      100       LocalVariableTable:
      101         Start  Length  Slot  Name   Signature
      102             0      30     0  args   [Ljava/lang/String;
      103             3      27     1     i   I
      104             6      24     2     j   I
      105            10      20     3     k   I
      106            14      16     4     s   Ljava/lang/String;
      107 }
      108 SourceFile: "PCRegisterTest.java"
    • 左边的数字代表指令地址(指令偏移),即 PC 寄存器中可能存储的值,然后执行引擎读取 PC 寄存器中的值,并执行该指令

          

    三、面试题

    3.1、使用PC寄存器存储字节码指令地址有什么用呢?  

    1. 因为线程是一个个的顺序执行流,CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行

    2. JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令

    3.2、PC寄存器为什么被设定为私有的?  

    1. 我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,如何保证分毫无差呢?

    2. 为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。

    3. 由于CPU时间片轮限制,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。

    4. 这样必然导致经常中断或恢复,如何保证分毫无差呢?每个线程在创建后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响。

  • 相关阅读:
    常用git命令
    复制文件
    实现斗地主洗牌、发牌、看牌
    线程池实现多线程
    git无法提交问题
    Js中处理日期加减天数
    form详解
    node.js中exports与module.exports的区别
    css的direction属性
    webstorm基础使用
  • 原文地址:https://www.cnblogs.com/h--d/p/14121605.html
Copyright © 2020-2023  润新知