跳到主要内容

02、JVM 实战 - JVM整体结构

jvm整体结构

主要讲述的是HotSpot虚拟机

 

类加载器

前端编译器
 

我们都知道java是一种跨平台的语言,那么java是如何实现这种平台无关性的呢?
任何一门编程语言都需要转换为与平台相关的汇编指令才能够最终被硬件执行,比如C和C++都将我们的源代码直接编译成与CPU相关的汇编指令或机器指令给CPU执行。

不同系列的CPU的体系架构不同,所以它们的汇编指令/机器指令也有不同,比如X86架构的CPU对应于X86汇编指令,arm架构的CPU对应于arm汇编指令。如果将程序源代码直接编译成与硬件相关的底层汇编指令,那么程序的跨平台性也就大打折扣,但执行性能相对较高

为了实现平台无关性,java的编译器javac并不是将java的源程序直接编译成与平台相关的汇编指令,而是编译成一种中间语言,即java的class字节码文件。字节码文件(里面是字节码,即一个一个的字节,是经过编译器预处理过的一种文件,是JAVA的执行文件存在形式。
它本身是二进制文件,但是不可以被系统直接执行,而是需要虚拟机(JVM)解释执行。由于被预处理过,所以比一般的解释代码要快,但是仍然会比系统直接执行的慢。字节码文件里面存的不是二进制,是十六进制,这是因为二进制太长了,一个字节要由8位二进制组成。所以用十六进制标表示,两个十六进制就可以表示一个字节。)

java源码编译后的字节码文件是不能够直接被CPU执行的,故为了让java程序能够在不同的平台上执行,java官方提供了针对于各个平台的java虚拟机,通过JVM来加载字节码文件,JVM运行于硬件层之上,屏蔽各种平台的差异性。javac编译后的字节码文件统一由JVM来加载,最后再由jvm里的执行引擎转化成与硬件相关的机器指令被CPU执行。

JVM自己定义了一套JVM层面的规范,在JVM层面抽象出一些我们能够认识的指令助记符,这些指令助记符就是java的字节码指令。将字节码中的每个字节和我们写的java源代码相关联,即java源代码对应于class文件中的哪段十六进制。
 
并不是所有高级语言都依次从高级语言到汇编语言再到机器语言,但一定是所有高级语言都要到机器语言,其过程可能各有不同

高级语言根据用途不同有很多分类,主要分为编译型语言和解释型语言。当然,也有二者的混合语信。
编译型语言
编译型语言包括C、C++、 Fortran、 Pascal、 Delphi 等。这里说的编译是指在应用源程序执行之前,就将程序源代码" 翻译”成汇编语言,
然后进一步根据硬件环境被变成符合运行需要的机器语言的目标文件。通过这种方式,使用比较方便、效率较高。但应用程序一旦需要修改, 
必须先修改源代码,再重新编译生成新的目标文件才能执行,只有目标文件而没有源代码,几乎是没法修改的。
大多数软件产品都是编译后发行给用户的,不仅便于直接运行,同时又使他人难于盗用其中的一些原始代码。

解释型语言
解释型语言包括Tcl、Perl、 Ruby、 VBScript、 JavaScript 等解释型语言的实现中,翻译器并不产生机器语言的目标文件,
而是产生易于执行的中间代码,这种中间代码与机器代码是不同的,中间代码的解释是由解释器软件支持的,不能直接使用硬件,
解释器软件通常会导致执行效率较低。用解释型语言编写的程序是由另一一个可以理解中间代码的解释程序执行的。与编译程序不同的是,
解释程序的任务是逐一将源程序的语 句解释成可执行的机器指令,不需要将源程序翻译成目标代码后再执行。解释程序的优点是当语句出现语法错误时,
可以立即引起程序员注意,而程序员在程序开发期间就能进行校正。每条语言只有在执行才被翻译。
这种解释型语言每执行一次就翻译一次,因而从某种程度上说效率比较低。一般地来说,如果你听别人说到动态语言,大多都是指解释型语言。

混合型语言
有一些人为了一些特殊的目的制造了一些四不像的混合型语言。比方说Java,Java程序也需要编译,但是没有直接编译成为机器语言,而是编译成为字节
码,然后在Java虚拟机上用解释方式执行字节码。这种运行方式带来了一些优势,但同时直接导致了复杂的环境、不算很高的效率和很多的争议。

汇编指令是CPU能真正执行的指令
汇编语言指令 如果放宽来讲还有汇编程序(或者说叫汇编编译器吧)能识别的伪指令
比如DB A 'A' 这就是一条伪指令
定义一个字节的数据
像mov ax,bx就是一条汇编指令,同时也是一条汇编语言指令
作用就是把bx的值赋值给ax

汇编指令和机器指令关系:
汇编指令是机器指令便于记忆的书写格式。同机器指令一一对应。

机器码(机器指令)是0和1组成的二进制序列,可读性极差
指令就是把特定的0和1序列,简化成对应的指令(一般为英文简写,如mov,inc等),可读性稍好
汇编语言包括指令和伪指令。伪指令是为了编程方便,对部分指令做的封装。

具体流程

1、 翻译程序可以分为两种情况:编译程序和解释程序;
 
2、 编译程序;
所谓编译程序,就是指将“源程序”一次性编译成“机器语言”,也即“可执行文件exe”。编译完成后,再执行该文件。例如C语言程序,就是先全部编译为机器语言再执行。(参看C语言的编译过程:预处理-编译-汇编-链接-装入)

3、 解释执行;
所谓解释执行,就是指对于“源程序”,翻译一条语句为机器语言,立即执行该语句,执行完成后,再继续翻译后面的一条语句,在执行。依次:翻译一条语句就立即执行一条语句。例如Python

特例:JAVA,一次编译,到处执行
JAVA并不完全相同于上述两种情况,Java引入了字节码文件(.class)以及JAVA虚拟机(JVM)的概念。详细来说,就是:java类似于C语言一次性把源程序编译完成,但是C程序是将源程序直接编译为机器语言了,而java程序则是编译为了字节码文件(.class)。随后,java的字节码文件在装载有JVM的机器上被解释为机器语言,再执行。
 

Java编译器将.java文件编译成字节码(.class文件),class文件中的代码是是一种中间代码,介于源程序与机器码之间,该字节码不能被除JVM外任何平台所理解,当JVM接收到字节码,它会识别出它所工作的平台,然后将字节码转换为原生的机器码。

总结来说:java的编译过程,相当于在c的编译过程中增加了一个环节,即从"源程序–>机器语言"变成了"源程序–>字节码文件–>机器语言",而字节码文件只有JVM才能识别理解。因此,这个新增的流程,使得java没有C/C++运行那么快速,但是新增了“跨平台性”,只要有jvm的机器都能执行字节码文件,也即“一次编译,到处执行”。

JAVA代码执行流程