【计算机组成原理】当一行java代码执行时,CPU揹着我们干了什么?

根据冯·诺依曼思想,计算机采用二进制作为数制基础,必须包含:运算器,控制器,存储设备,输入输出设备:
在这里插入图片描述

首先抛开主题,来分析分析cpu的基本工作原理:现代cpu芯片中大部分都集成了控制单元,运算单元,存储单元。控制单元是cpu的控制中心,cpu需要通过控制单元才能知道下一步做什么,也就是执行什么样的指令。控制单元又包括:
指令寄存器(IR),指令译码器(ID)和操作控制器(OC)。

当程序被加载进内存之后,指令就在内存中。指令指针寄存器IP指向内存中的下一条等待执行的指令的地址,控制单元根据IP寄存器的指向,将主存中的指令转载到指令寄存器,这个指令寄存器也是一个存储设备,不过是集成于CPU内部。

首先要明白指令从主存到达CPU后只是一串010111001的二进制串,还需要通过译码器进行解码,分析出操作码是什么,操作数在哪里?之后就是具体的运算单元来进行算术运算(加减乘除),逻辑运算。总之一句话:CPU指令执行的过程为
取址(内存中取操作树放入到寄存器)——>译码(从主存中获取操作数放入高速缓存L1中)——>执行(运算)
所有的指令都会按照这个顺序来进行执行。但是多个指令之间其实是可以并行的,对於单核CPU来说,同一个时刻执行单元最多只能被一条指令所占有。也就是上述中的执行只能串行执行。所以为了提升CPU的指令执行速度。

需要保证在运算单元在执行前的准备工作都已经完成,这样就不会使运算单元存在经常性的等待。而在刚刚的串行流程中,取指,解码的过程中,运算单元处于空闲状态,而且如果取指和解码的过程中没有命中,高速缓存还需要从主存中取,而主存的速度和CPU的速度不在一个级别上。所以,指令流水线可以大大的提高CPU的处理速度,如下图,下图是一个三级流水线的示意图,现在的奔腾CPU基本上都是32级的流水线。
在这里插入图片描述
当然,除了指令流水,CPU还分支预测,乱序执行等优化手段,具体《计算机组成原理》中都有介绍。

那么,一行java代码是如何执行的?
一行代码能够执行,必须要有可以执行的上下文环境,包括,指令寄存器,数据寄存器,栈空间等内存资源,然后这行代码必须作为一个执行流能够被操作系统的任务调度器识别,并给他分配 CPU 资源,当然这行代码所代表的指令必须是 CPU 可以解码识别的,所以一行 Java 代码必须被解释成对应的 CPU 指令才能执行.下面我们看下System.out.println(“Hello world”)这行代码的转译过程.

Java 是一门高级语言,这类语言不能直接运行在硬件上,必须运行在能够识别 Java 语言特性的虚拟机上,而 Java 代码必须通过 Java 编译器将其转换成虚拟机所能识别的指令序列,也称为 Java 字节码,之所以称为字节码是因为 Java 字节码的操作指令(OpCode)被固定为一个字节,以下为 System.out.println(“Hello world”) 编译后的字节码

0x00:  b2 00 02         getstatic  Java .lang.System.out
0x03:  12 03            ldc "Hello, World!"
0x05:  b6 00 04         invokevirtual  Java .io.PrintStream.println
0x08:  b1               return

最左边是偏移,中间是给虚拟机读取的字节码,右边是对应的高级语言代码。下面是通过汇编语言转换成的机器指令。中间是机器码,第三列为及其指令。最后一列对应汇编代码。

0x00:  55                    push   rbp
0x01:  48 89 e5              mov    rbp,rsp
0x04:  48 83 ec 10           sub    rsp,0x10
0x08:  48 8d 3d 3b 00 00 00  lea    rdi,[rip+0x3b]
                                    ; 加载 "Hello, World!
"
0x0f:  c7 45 fc 00 00 00 00  mov    DWORD PTR [rbp-0x4],0x0
0x16:  b0 00                 mov    al,0x0
0x18:  e8 0d 00 00 00        call   0x12
                                    ; 调用 printf 方法
0x1d:  31 c9                 xor    ecx,ecx
0x1f:  89 45 f8              mov    DWORD PTR [rbp-0x8],eax
0x22:  89 c8                 mov    eax,ecx
0x24:  48 83 c4 10           add    rsp,0x10
0x28:  5d                    pop    rbp
0x29:  c3                    ret

JVM 通过类加载器加载 class 文件里的字节码后,会通过解释器解释成汇编指令,最终再转译 成 CPU 可以识别的机器指令,解释器是软件来实现的,主要是为了实现同一份 Java 字节码可 以在不同的硬件平台上运行,

而将汇编指令转化成机器指令由硬件直接实现,速度非常快,当然,大部分JVM为了提高自身的运行效率,将某些热点代码(一个方法中的代码)一次全部编译成机器指令然后执行,也就是和解释执行对应的即时编译(JIT), JVM 启动的时候可以通过 -Xint 和 -Xcomp 来控制执行模式.具体自己可以操作一波。
从软件层面上看,class文件被加载进虚拟机后,类信息会存放在方法区,在实际运行的时候会执行方法区中的代码,在JVM中的所有线程共享堆内存和方法区,而每个线程有自己独立的java方法栈。

本地方法栈(面向 native 方法),PC寄存器(存放线程执行位置),当调用一个方法的时候, Java 虚拟机会在当前线程对应的方法栈中压入一个栈帧,用来存放 Java 字节码操作数以及局部变量,这个方法执行完会弹出栈帧,一个线程会连续执行多个方法,对应不同的栈帧的压入和弹出,压入栈帧后就是 JVM 解释执行的过程了.
在这里插入图片描述参考博文:
https://time.geekbang.org/column/article/11289
https://blog.csdn.net/u013635487/article/details/61413589
https://blog.csdn.net/dong_daxia/article/details/80289951

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章