Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker DevOps


JVM:运行时数据区之程序计数器

Java JVM 大约 5258 字

名称

程序计数器、PC寄存器、Program Counter Register

作用

用来存储指向下一条指令的地址,也即:将要执行的指令代码。由执行引擎取下一条指令。

概念

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

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

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

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

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

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

示例代码

public class PCRegisterDemo {

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

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

}

字节码

程序寄存器工作流程:

  1. 0: bipush 10:压栈10
  2. 2: istore_1 :保存10
  3. 3: bipush 20:压栈20
  4. 5: istore_2 :保存20
  5. 6: iload_1 :加载10
  6. 7: iload_2 :加载20
  7. 8: iadd :相加10+20
  8. 9: istore_3 :保存相加结果
  9. 10: ldc #2:对应常量池#2#2 = String #27#2又对应#27#27 = Utf8 abc
  10. 12: astore 4:保存abc
Classfile PCRegisterDemo.class
  Last modified 2021年6月27日; size 641 bytes
  MD5 checksum 019fc9cb21b2d56b1d375dfcd1c55c1e
  Compiled from "PCRegisterDemo.java"
public class PCRegisterDemo
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #5                          // PCRegisterDemo
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #6.#26         // java/lang/Object."<init>":()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            // PCRegisterDemo
   #6 = Class              #33            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LPCRegisterDemo;
  #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               a
  #23 = Utf8               Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               PCRegisterDemo.java
  #26 = NameAndType        #7:#8          // "<init>":()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               PCRegisterDemo
  #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 PCRegisterDemo();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LPCRegisterDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) 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 7: 0
        line 8: 3
        line 9: 6
        line 11: 10
        line 12: 14
        line 13: 21
        line 14: 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     a   Ljava/lang/String;
}
SourceFile: "PCRegisterDemo.java"

相关问题

问题一

问:使用PC寄存器存储字节码指令地址有什么用?

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

问题二

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

答:CPU不停切换,导致任务经常中断或恢复,每个线程记录一份当前线程执行的代码位置,争取到CPU时间片的线程读取PC寄存器中保存的下一条代码位置继续执行。每个线程一份保证每个线程读取到的都是线程自身执行到的代码位置而不是其他线程的。

阅读 418 · 发布于 2022-05-22

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批