首页 > 编程语言 >一文理解JVM的程序计数器(PC)

一文理解JVM的程序计数器(PC)

时间:2023-02-22 16:33:06浏览次数:36  
标签:PC 计数器 指令 线程 JVM 寄存器 执行

目录

1 功能演示

2 跳转、循环等执行的执行原理

3 关于PC的面试题

JVM中的程序计数寄存器(Program Counter Register)中,Register的命名源于CPU的寄存器,寄存器存储指令相关的信息。CPU只有把数据装载到寄存器才能够运行。但是这里并非是广义上所指的物理寄存器,在JVM中只是对PC寄存器的一种模拟,用来处理当前线程相关指令的计数器。

有一点与CPU的寄存器是类似的,那就是占用空间小,但运行速度最快。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,因此它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

在JVM规范中,每个线程都有它自己的程序计数器,而且是私有的,生命周期也与线程的生命周期一致。任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。

需要注意的是PC区是唯一一个没有OutofMemoryError情况的区域。而本地栈等结构没有垃圾回收,但是有可能溢出。

本文我们将介绍PC计数器是如何工作的,如何表示跳转和循环等操作的,以及线程安全相关的问题。

 

1 功能演示
public class PCRegisterTest {
   public static void main(String[] args) {
      int i = 10;
      int j = 20;
      int k = i + j;
   }
}
编译之后,执行javap -v PCRegisterTest.class,查看字节码,其中与PC有关的是:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, 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: return
上面的竖排0 到10,就可以理解为PC计数器,而右侧的istore_1等就是操作指令,如下图。

 

 

 

 

执行的时候如果PC发出的指令序号是5,那么这里就会执行存储操作,然后会执行操作局部变量表,操作数栈等等,最后还要再转换成机器指令给CPU来执行。

现在有个问题 ,PC计数器怎么知道要执行哪条指令的呢?PC计数器的值是谁来改的呢?修改PC值的是执行引擎。如下图所示。每执行一次,执行引擎就会修改该值来通知当前线程接下来要做什么工作。可以简单将PC理解为一个调度员。

 

 

 

 

2 跳转、循环等执行的执行原理
如果我们增加代码,会发现上面的Code下的指令会持续增加,对应的就是将每条语句都处理成了能够执行的指令。我们现在思考一个问题跳转、循环等功能,PC指令如何表示以及执行的呢?

先说结论:跳转还是循环是在字节码的指令里设置好的,PC计数器执行的时候不管这些,只关心接下来要执行哪一条指令。

我们先看一下跳转结构:

public class PCRegisterTest {

public static void main(String[] args) {
   int i = 30;
   if (i < 30) {
      i++;
   } else {
      i--;
   }
  }
}
我们通过javap -v来查看生成的PCRegisterTest.class文件的信息:

 

 

 

 

可以看到,这里的指令6是一个比较指令

6: if_icmpge 15
表示的含义是,如果比较只会满足要求就继续向下执行,否则就跳转到指令15执行。

我们可以看到,如果满足条件继续执行,那就是iinc就是累加1指令。如果跳到指令15,那就是iinc指令执行累减1指令,因此与我们的代码设计是一致的。

我们再来看一下循环的情况:

public class PCRegisterTest {
   public static void main(String[] args) {
      int i = 30;
      int j = 40;
      for (i = 10; i < j; ) {
         int k = i + j;
      }
   }
}
在main()方法位置会看到如下信息:

 

 

 

 

可以看到,生成的指令里,11号位置有个比较,如果满足才会继续向下走,如果不满足就会直接跳到21行。

我们注意到11行之下的正好是循环体的内容,而21行正好是退出。因此这里PC计数器仍然可以无脑向下走,不用关心是否为循环,只要知道下一个位置就可以了。

我们再看一下do while循环是否也这样:

public class PCRegisterTest {
   public static void main(String[] args) {
      int i = 30;
      do {
         i++;
      } while (i < 40);
   }
}
生成的指令如下:

 

可以看到,指令9有个比较指令if_icmplt,如果满足就跳到指令3位置继续执行,否则就继续执行指令12。而指令3的位置正好就是累加操作i++,因此完美实现了循环。

3 关于PC的面试题
问题1: 使用PC寄存器存储字节码指令地址有什么用呢?

这是因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。具体来说就是JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。

2.PC寄存器为什么被设定为私有的?

所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
————————————————
原文链接:https://blog.csdn.net/xueyushenzhou/article/details/127596969

标签:PC,计数器,指令,线程,JVM,寄存器,执行
From: https://www.cnblogs.com/lqmblog/p/17144831.html

相关文章

  • 如果个人pc上要装不同社区版本的pycharm,安装时需要注意的一点
    pycharm下载地址[包含了目前发行的所有版本]:https://www.jetbrains.com/pycharm/download/other.html选择指定的版本,点击     勾选uninstallsilently(sett......
  • Jvm类加载机制
         其中loadClass的类加载过程有如下几步:加载>>验证>>准备>>解析>>初始化>>使用>>卸载加载:在硬盘上查找并通过IO读入字节码文件,使用到类时......
  • 【java 基础】代码在jvm的内存运行流程分析总结
    堆:存储new出来的对象(包括成员变量、数组、方法的地址)栈:正在调用的方法中的局部变量(包括方法的参数)方法区/元空间:.class字节码文件(包括所有方法)publicclassStudentTe......
  • JVM系统优化实践(2):JVM内存与回收
    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~   上次把JVM的类加载过程粗略地过了一遍,今天再来看看JVM运行代码时,系统里发生了什么。就像家里的柴、米、油、盐......
  • 主成分分析(PCA)
    目录​​方差​​​​协方差​​​​维度灾难​​​​主成分分析(PCA)​​​​一、标准化​​​​二、计算协方差矩阵​​​​三、计算出主成分​​​​主成分是什么​​​​......
  • jvm内存泄露分析方法
         场景:使用监控工具发现内存使用不断增加,则有可能存在内存泄露,内存泄露可以使用jmap工具进行分析。jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆......
  • .net core(.net 6) gRPC服务搭建
    1、搭建gRPC服务端1.创建项目使用VS2022创建gRPC服务项目使用.net6.0框架 得到如图红框内结构 2.编写gRPC接口 .proto接口文件:gRPC支持多语言,在定义接口时需......
  • 什么是RPC协议
    工作的时候,第一次接触CRPC协议,当时就很懵,啥是CRPC协议,一脸懵逼,于是就到网上去搜,填充知识空缺。不少解释显得非常官方,我相信大家在各种平台上也都看到过,解释了又好像没解释......
  • 规则LDPC和不规则LDPC译码算法MATLAB仿真
    up目录一、理论基础二、核心程序三、测试结果一、理论基础LDPC码是根据低密度稀疏校验矩阵H来构造的。假设需要发送一组信息T{t_1,t_2,⋯,t_n},在发送前先使用生成矩......
  • 使用FTPClient封装FtpUtil
    1.新增POM依赖<!--文件上传--><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.......