为什么JVM可以被称为机器 (Machine)
“学而时习之不亦说乎”,今天我们借着讨论标题这个问题,将JVM的全貌展示出来。
如果你是一名Java Coder,那么你一定听说过 “Write Once, Run Anywhere”,翻译过来就是一次编写到处运行。可能以现在的眼光来看,这不是很多语言都可以吗?但是站在当时,这可是很厉害的能力了。当时的操作系统至少存在两个阵营,一个是Windows,一个是Linux,由于系统不同,提供的系统库API也不一样,这就导致在写C语言代码的时候,尤其使用到系统库的时候都不得不做兼容处理。虽然当时C语言也说自己是跨平台语言,但是这个跨平台跨的是CPU平台,即可以无视CPU的架构,即无论是ARM架构(精简指令集),还是Intel架构(复杂指令集)但是无法无视操作系统的不同。而Java这门编程语言却可以做到,即既可以无视CPU平台,也可以无视操作系统平台。那么它是如何做到的呢?
答案就是JVM,JVM全称为:Java Virtual Machine,翻译过来就是Java虚拟机。但是从本质上来说它只是一个普普通通的进程。进程嘛,就是运行中的程序,程序肯定实现了某些功能。JVM自然也不例外,只不过它实现的功能比较有意思,即模拟出了操作系统运行程序的逻辑。也正是因为这样,它才被叫做虚拟机。回顾一下,操作系统是如何执行我们的程序的
第一步:加载程序,将程序从磁盘中加载到内存中,并分配一片内存(堆空间,栈空间,代码空间),创建一个数据结构PCB。
第二步:运行程序,操作系统调度这个进程,根据PC寄存器运行对应的代码。
运行一个程序至少需要这两步,而JVM就模拟了这两步。操作系统在加载程序的时候,加载的是可执行文件,而JVM加载的是类字节码,而这加载的组件也被叫做类加载器。从磁盘中被加载到内存中的字节码需要有地方存放,存放的位置有一个专门的名字叫做元数据区(方法区),当然也会创建 堆空间,栈空间。元数据区,堆区,栈区,程序计数器寄存器(PC寄存器)被统称为运行时数据区。这样JVM就模拟完了第一步。然后进入到第二步运行程序,JVM根据PC寄存器将对应的字节码指令交给执行引擎,执行引擎根据字节码指令规范,将字节码翻译成对应的操作系统认识的机器码,从而让CPU执行这些机器码,以达到运行程序的目的。
我还是举个例子方便理解,比如下面这个代码
class Main {
public static void main(String[] args) {
f1("hello");
System.out.println("exit...");
}
public static void f1(String word) {
System.out.println(word + " pandaer");
}
}
要想运行这段代码,我们至少需要执行两个命令
- 编译成字节码 javac Main.java
- 运行Java字节码 java Main
我们来看看这两步分别干了什么,第一步使用了Java的编译器,将一个文本文件的代码编译成了JVM认识的字节码文本文件。第二步的java命令,比较复杂,听我慢慢到来
当我们在控制台输入java Main
的时候,java命令会首先创建一个JVM进程出来,这个JVM进程根据java命令后面跟的参数 Main
去寻找对应包(由于Java的规定,包名与目录名一致)下的Main.class
文件,并利用类加载器将Main.class
文件加载进JVM进程中的元数据区,然后创建一个主线程,将这个字节码文件的main方法的地址赋值给这个线程的PC寄存器,然后这个主线程将PC寄存器的值交给执行引擎,执行引擎开始执行字节码指令,比如上面的代码,一开始就执行main方法,所以就会在栈空间上开辟一块新的栈帧,存放一些局部变量,这里便是字符串”hello”
的地址,然后遇到f1函数,就会又开辟一块新的栈帧并压栈到栈空间中,然后执行这个函数,即输出”hello pandaer”
然后这个函数执行完毕,返回到main函数,同时f1函数的栈帧出栈。继续执行main函数的代码,即输出”exit…”
然后退出main函数,主线程结束,然后整个JVM进程结束。
相信到此,你就可以回答为什么JVM可以被称为机器了,主要就是因为JVM模拟了操作系统执行程序的逻辑。到此呢,你已经知道了JVM运行的全貌了,有了全貌之后,再去了解JVM细节的时候就不至于迷路了,对于JVM更加细节的知识,网上以及有很多优质的资源了,我不想再重复了,不过如果你想深入理解Java虚拟机的话,周志明老师的《深入理解Java虚拟机》就非常不错。