语言的本质
应用程序 → 语言 → 源码 → 汇编码(平台相关) → 机器码(平台相关) → 操作系统(内核) → cpu(不同指令集和不同的执行特性)
应用程序的本质,其都是 数据 + 算法,数据是这个应用运行过程中,所需要的一切的信息,小到一个方法中局部变量的声明,大到整个数据段,代码段。
CPU
对于cpu而言,它所做的事情就是访问数据(L0-L2三级缓存、寄存器、主存)和运算
将数据从内存(寄存器)中加载出来,放到相关寄存器,然后执行运算,得到结果后,放回寄存器(内存)。
这其中又包括
- 数据的访问,即对内存、寄存器的访问
- 指令的执行。
对数据的访问,又是一个进化史,从最初的实模式,直接访问物理内存地址,再到后来为了适应多任务,又到了保护模式的分段,这时候cpu把内存分成了段,有代码段、数据段之分,不同进程可以定义不同的段边界,隔离不同进程之间的数据,再往后又引入了MMU(内存管理单元),这时,则是进入了分页访问模式。
但是万变不离其宗,其核心还是,找到cpu指令,访问所需的内存,做计算,将数据放回去(包括 主存、磁盘、鼠标、键盘等所有IO设备数据的存取)
操作系统
操作系统,本质上也只是一个特殊的应用程序,比如操作系统内核,在我以往的认知中,我一直认为操作系统内核是一个单独的进程,常驻在后台执行内核该执行的任务,比如读、写磁盘,比如更新系统时间。
而实际上,操作系统内核存在的形态,并不完全是我想象中的样子,尤其是内核,它其实很混沌。
当我们摁下电源键时,硬件会被激活,首先是启动BIOS,BIOS是一个独立的小系统,它包含硬件和软件部分,它所做的事情也很原始,当它启动时,它会初始化物理内存中的数据,然后会根据一些约定,从磁盘的固定位置,加载操作系统程序,注意,这整个过程,都是cpu在进行数据的访问和运算,然后cpu的指令寄存器的地址,会指向内存中,操作系统程序代码(这时候已经是cpu指令了)的位置,这时可以认为,操作系统,正式启动,cpu开始执行操作系统代码。
操作系统就像一个管家,操作系统的代码,就是统管整个计算器的所有资源(数据),它所做的事情,其实就是在cpu的这套执行框架上,构建出一个五彩斑斓的大世界,但是其本质,也不过是对数据的操作。
比如,很经典的一件事情,就是操作系统,对于进程概念、虚拟内存的实现,就是通过一些巧妙的方式,来抽象出来的,比如在cpu的角度看,它永远只知道,当前执行的指令是什么,它不知道什么是进程。
进程的本质,不过是操作系统,在cpu执行指令的过程中,对cpu的戏耍(当然,能做到也还要归根与另一个cpu层面就支撑的特性:中断),原本cpu正要执行指令1,下一秒cpu就发现要执行的指令变成了指令2,其最最本质,不过就是对pc寄存器的数值,做了下改变。
语言
java源码 → jvm字节码 → 汇编码 → 机器码(cpu指令) → cpu
其实对于任何一个语言而言,其最终,都是cpu指令,也都是一样的cpu 访问数据和运算。
那对于java而言,它是如何执行的?
通常情况是,
-
启动jvm程序,jvm程序会具备一块虚拟的内存空间,它会在这个内存空间中,做好内存的划分和数据的初始化,如GC线程的启动,堆、栈、元空间内存区域的分配等。
-
加载应用程序,jvm需要找到应用程序所代表的class文件(数据、代码),进而将其加载进内存,并找到main方法,并开始执行。
-
重点在于,这执行class文件中的代码的本质是什么?
class文件中的代码,就是字节码。 Jvm中有一个模块,专门用于解析字节码,将其转换和编译为当前平台、cpu兼容的指令,并交给cpu执行。
那么现在的问题就回到了,如何解析字节码,这是个很有意思的地方
比如字节码的创建对象 new ,包含这些操作
- 分配堆内存空间
- 执行对象字段的初始化(比如执行构造方法)
- 返回对象的内存地址
在字节码中就是一条语句,事实上,其涉及到很多条cpu指令,而这就是jvm的字节码执行引擎所做的事情,它会把这一条 **创建对象的字节码指令 转换为 对相关数据进行访问和处理的 cpu指令,**然后调用这些指令,至于如何调用?我觉得应该是有一块内存区域,专门存放转换后的cpu指令,在转换完后,将cpu的pc寄存器,指向那块区域就可以了,当然这其中还涉及到线程栈、方法调用等各种概念,但这些都是基于 cpu 指令 执行的这个本质抽象出来的。