1 概述
JVM 这块,总体而言,既简单,又复杂。简单是因为它的职责很明确,逻辑结构很清晰。复杂是因为一旦涉及具体实现,其中细节极多,而且知识点比较零碎,难以形成体系。
1.1 什么是 JVM
当我们在说 JVM 时,我们可能在说下面三种不同的东西:
- JVM 规范:抽象的规范,不具体指哪个 JVM 产品;
- JVM 实现:只要符合 JVM 规范的都可以算是正确的 JVM 实现,比如 HotSpot;
- JVM 实例:一个 JVM 进程。
具体,要根据语境领会。
类比:
//JVM 规范
public interface JVM {
void loadClassFile();
void runMain();
}
//JVM 实现
public class HotSpot implements JVM {
public void loadClassFile() {
//...
}
public void runMain() {
//...
}
public static void main(String[] args) {
//JVM 实例
Jvm jvm = new HotSpot();
jvm.loadClassFile();
jvm.runMain();
}
}
1.2 JVM,JRE ,JDK 三者的关系
一句话概括:
JDK = JRE + (javac + java + 其它)= JVM + (标准 jar 包 + 第三方 jar 包 + 其它) + (javac + java + 其它)。
1.3 平台无关性、语言无关性
平台无关性:
- 不是说 JVM,JVM 直接与平台打交道不可能平台无关;
- 说的是 Java(或其它跑在 JVM 上的语言),由于 JVM 帮助屏蔽了平台差异而具有了平台无关性。
语言无关性:
- 说的是 JVM,原本是为了支持 Java,所以叫 Java 虚拟机;
- 但 Java 虚拟机只认字节码,不管源码是不是 Java 写的,甚至不管字节码是从哪来的。
1.4 为什么要用 JVM
任何面向人类设计的高级编程语言,都不可能直接拿到计算机上去执行,计算机看不懂。所以,必须把高级编程语言翻译成计算机能懂的机器语言。这个翻译过程可以有两种:(1)一次性直接编译到机器语言;(2)只编译到中间某一步,把中间产物(如字节码)拿到虚拟计算机(比如 JVM)上执行。
所以:
- Java 并非一定要在 JVM 上执行,编译成机器语言直接拿到计算机上执行也不是不行,前提是要开发这样的编译器;
- 非 JVM 语言,如 C++ 也不是一定不能在 JVM 上执行,也可以开发编译器把 C++ 源码编译成字节码。
用 JVM 的好处:
- 可移植、跨平台,通过字节码将源码与执行解耦,源码可以也可能被拿到任何可运行 JVM 的平台上执行;
- 代码托管、简化,通用逻辑放到 JVM 统一处理,如内存管理、数组越界检测、空指针检测等,减少开发工作量,提高安全性。
用 JVM 的坏处:
- 性能基本由 JVM 决定;
- 开发人员权限较低,无法控制代码执行细节。
所以,如果需要 JVM 带来的好处,而对它带来的不便不是很在意,那就可以选择跑在 JVM 上。
1.5 JVM 运行过程
JVM 运行大体流程很简单:
- 加载:从外界读取字节码到 JVM 内部;
- 解析:从字节码中解析出必要的信息存储到内存相应区域;
- 执行:从 main 方法开始执行 JVM 指令;
- 退出:所有前台线程执行完退出,或 JVM 中途崩溃。
但其中细节极其复杂,此处暂时不提。
1.6 JVM 结构
根据 JVM 的运行过程,可以将 JVM 划分为以下几个部分:
- 类加载系统:负责加载字节码文件,并解析出类信息;
- 内存:负责存储类信息,以及运行时中间数据;
- 执行引擎:负责执行 JVM 指令。
同样其中细节极其复杂,此处暂时不提。
1.7 公有设计、私有实现
公有设计:JVM 规范统一规定了 JVM 必须要做到的事情,如:
- 必须能正确读取 class 文件中的语义;
- 必须能正确执行 class 文件中的指令。
私有实现:JVM 实现者在完成 JVM 规定的基础上自由发挥,如:
- 解释执行 JVM 指令,或者编译执行,或者两者混合,都可以;
- JVM 规范设计了方法区、堆、栈等,实现者可以把方法区实现在堆外,也可以就在堆内划出一块区域作为方法区。