您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

程序计数器(PC寄存器)

Dongguo丶 发布时间:2021-08-28 00:25:47 ,浏览量:2

PC Register介绍

image-20210826124156451

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

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

image-20210826124540291

栈中每个栈帧都有行号标识,PC寄存器就相当于这个行号标识

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

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

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

它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成

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

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

举例说明
package com.dongguo.jvm04;

/**
 * @author Dongguo
 * @date 2021/8/26 0026-12:58
 * @description:
 */
public class PCRegisterTest {
    public static void main(String[] args) {
        int i = 10;
        int j = 20;
        int k = i + j;
    }
}

运行,使用javap 进行PCRegisterTest.class反编译

E:\workspace\JVMDemo\target\classes\com\dongguo>cd jvm04

E:\workspace\JVMDemo\target\classes\com\dongguo\jvm04>javap -v PCRegisterTest.class
...
Code:
      stack=2, locals=4, args_size=1
         0: bipush        10//取值10
         2: istore_1//将i存到索引为1的位置
         3: bipush        20//取值20
         5: istore_2//将j存到索引为2的位置
         6: iload_1//取出i
         7: iload_2//取出j
         8: iadd//相加
         9: istore_3//将k保存到索引为3的位置
        10: return//结束
...

左侧0、2、3、5、6、7、8、9、10就是指令地址(偏移地址),这个就是PC寄存器中存储的结构

中间部分为操作指令

image-20210826132213477

再增加其他类型的代码查看

package com.dongguo.jvm04;

/**
 * @author Dongguo
 * @date 2021/8/26 0026-12:58
 * @description:
 */
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);
    }
}

重新编译后

Constant pool:
   #1 = Methodref          #6.#26         // java/lang/Object."":()V
   #2 = String             #27            // abc
   #3 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #30.#31        // java/io/PrintStream.println:(I)V
   #5 = Class              #32            // com/dongguo/jvm04/PCRegisterTest
   #6 = Class              #33            // java/lang/Object
   #7 = Utf8               
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/dongguo/jvm04/PCRegisterTest;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               i
  #19 = Utf8               I
  #20 = Utf8               j
  #21 = Utf8               k
  #22 = Utf8               s
  #23 = Utf8               Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               PCRegisterTest.java
  #26 = NameAndType        #7:#8          // "":()V
  #27 = Utf8               abc
  #28 = Class              #34            // java/lang/System
  #29 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #30 = Class              #37            // java/io/PrintStream
  #31 = NameAndType        #38:#39        // println:(I)V
  #32 = Utf8               com/dongguo/jvm04/PCRegisterTest
  #33 = Utf8               java/lang/Object
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (I)V
{
  public com.dongguo.jvm04.PCRegisterTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/dongguo/jvm04/PCRegisterTest;

  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   //从常量池中取出常量,#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

两个常见问题 使用PC寄存器存储字节码指令地址有什么用呢?/为什么使用PC寄存器记录当前线程的执行地址呢?

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

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

PC寄存器为什么会被设定为线程私有

我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法, CPU会不停地做任务切换,这样必然导致经常中断或恢复,如何保证分毫无差呢?**为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,**这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。

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

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

CPU时间片

CPU时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片。

在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.1216s