第一章:JVM 入门与概览

2 阅读5分钟

JVM 入门与概览:Java 程序到底是如何运行起来的?

本文是 JVM 系列第一篇,适合 Java 初学者以及工作 1~3 年的后端开发工程师阅读。


一、前言

很多 Java 开发者每天都在写代码:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello JVM");
    }
}

程序运行后输出:

Hello JVM

看起来非常简单。

但实际上,在这行代码背后,JVM 做了大量工作:

  • 加载类文件
  • 验证字节码
  • 分配内存
  • 创建线程
  • 执行方法
  • 回收垃圾对象
  • 编译热点代码

很多面试官喜欢问:

Java 为什么能够跨平台?

JVM 和 JDK 有什么区别?

Java 程序到底是如何运行起来的?

本文将带你从零开始理解 JVM。


二、什么是 JVM?

JVM 全称:

Java Virtual Machine(Java 虚拟机)

它本质上是一个运行 Java 字节码(ByteCode)的虚拟计算机。

简单理解:

Java代码
    ↓
编译
    ↓
.class字节码
    ↓
JVM执行
    ↓
操作系统
    ↓
CPU

例如:

int a = 10;
int b = 20;

System.out.println(a + b);

Java 编译器(javac)不会直接生成 CPU 可以执行的机器码。

而是生成:

Main.class

然后 JVM 再负责解释或编译执行。


三、为什么 Java 能跨平台?

Java 有一句经典口号:

Write Once, Run Anywhere

一次编写,到处运行

原因就在 JVM。


C语言运行方式

C源码
 ↓
编译
 ↓
Windows机器码
 ↓
Windows运行

如果换成 Linux:

C源码
 ↓
重新编译
 ↓
Linux机器码
 ↓
Linux运行

必须重新编译。


Java运行方式

Java源码
 ↓
javac
 ↓
字节码(.class)
 ↓
JVM
 ↓
操作系统

只要安装对应平台 JVM:

Windows JVM
Linux JVM
Mac JVM

同一个 class 文件都能运行。

例如:

Hello.class

既可以在:

  • Windows
  • Linux
  • MacOS

直接运行。

这就是 Java 跨平台的核心原因。


四、JDK、JRE、JVM 三者关系

这是面试高频题。

很多同学工作几年都说不清。


JVM

Java 虚拟机

负责:

  • 加载字节码
  • 执行字节码
  • 内存管理
  • 垃圾回收

简单理解:

JVM = Java运行引擎

JRE

Java Runtime Environment

Java运行环境

包含:

JVM
+
Java核心类库

例如:

String
ArrayList
HashMap

这些类都来自 JRE。


JDK

Java Development Kit

Java开发工具包

包含:

JDK
 ├─ JRE
 │   └─ JVM
 │
 ├─ javac
 ├─ javadoc
 ├─ jstack
 ├─ jmap
 ├─ jstat
 └─ jconsole

因此:

JDK > JRE > JVM

关系图:

JDK
│
├── JRE
│    │
│    ├── JVM
│    └── Java基础类库
│
├── javac
├── jmap
├── jstack
└── jstat

五、Java 程序运行全过程

下面是 JVM 最核心的流程。


第一步:编写源码

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello JVM");
    }
}

保存:

Main.java

第二步:编译

执行:

javac Main.java

生成:

Main.class

第三步:加载字节码

执行:

java Main

JVM启动。

此时:

ClassLoader
↓
加载 Main.class

进入 JVM 内存。


第四步:验证字节码

JVM 会检查:

字节码是否合法
格式是否正确
是否存在安全风险

例如:

非法修改class文件

会直接拒绝加载。


第五步:创建运行时内存

JVM 创建:

方法区
堆
虚拟机栈
程序计数器
本地方法栈

后面章节会详细讲解。


第六步:执行 main 方法

执行引擎开始工作:

main()
 ↓
println()
 ↓
输出结果

最终:

Hello JVM

出现在控制台。


六、JVM 核心组成部分

很多人学习 JVM 时容易迷失。

实际上 JVM 核心模块只有几个。


整体结构

                Java Program
                      │
                      ▼
              Class Loader
                      │
                      ▼
          Runtime Data Area
                      │
                      ▼
            Execution Engine
                      │
                      ▼
               Native Method
                      │
                      ▼
                 Operating System

七、类加载器(Class Loader)

作用:

加载class文件

例如:

new User();

JVM发现:

User.class

还没加载。

于是:

ClassLoader
↓
加载User.class
↓
放入内存

供程序使用。


八、运行时数据区(Runtime Data Area)

这是 JVM 内存区域。

包含:

程序计数器
虚拟机栈
本地方法栈
堆
方法区

后续章节会重点讲。

先简单理解:


堆(Heap)

对象存储区。

例如:

new User();
new ArrayList();
new HashMap();

全部放在堆中。


栈(Stack)

方法运行区域。

例如:

public void test() {
    int age = 18;
}

变量:

age

存在栈中。


九、执行引擎(Execution Engine)

执行引擎负责真正运行代码。

主要有两种方式:


解释执行

逐行翻译执行:

字节码
 ↓
解释器
 ↓
机器码

优点:

启动快

缺点:

执行慢

JIT编译执行

热点代码:

for(int i=0;i<10000000;i++)

执行很多次后。

JVM发现:

这段代码经常运行

于是:

字节码
 ↓
JIT
 ↓
机器码

直接编译。

以后直接执行机器码。

速度大幅提升。


十、垃圾回收器(GC)

Java 最伟大的设计之一。

开发者不用:

free()
delete

手动释放内存。

例如:

User user = new User();

user = null;

对象失去引用。

GC 会自动回收。


GC 负责:

发现垃圾对象
回收内存
整理空间
避免内存泄漏

后面我们会单独用数篇文章讲解。


十一、为什么 JVM 如此重要?

很多程序员认为:

我只写业务代码,不需要学 JVM。

实际上:

当线上出现问题时:

CPU飙高
频繁GC
内存溢出
接口超时
线程阻塞
服务卡死

最后都绕不开 JVM。

例如:

OutOfMemoryError
StackOverflowError
GC Overhead Limit Exceeded
Metaspace OOM

这些都是 JVM 问题。

高级开发工程师必须掌握。


十二、面试高频问题

1 JVM 是什么?

Java Virtual Machine,负责加载和执行 Java 字节码。


2 Java 为什么跨平台?

因为 JVM 屏蔽了底层操作系统差异。

.class
↓
JVM
↓
Windows/Linux/Mac

3 JDK、JRE、JVM 的关系?

JDK > JRE > JVM
JDK = 开发工具 + JRE
JRE = JVM + Java类库
JVM = 虚拟机

4 JVM 的核心组成?

类加载器
运行时数据区
执行引擎
本地方法接口
垃圾回收器

5 JVM 和 JMM 有什么区别?

JVM:

Java Virtual Machine

Java虚拟机。

JMM:

Java Memory Model

Java内存模型。

主要解决:

线程可见性
有序性
原子性

问题。


总结

本文我们建立了 JVM 的整体认知框架:

Java源码
 ↓
javac编译
 ↓
.class字节码
 ↓
ClassLoader加载
 ↓
运行时数据区
 ↓
执行引擎
 ↓
JIT优化
 ↓
GC回收

掌握这一张图,后续学习 JVM 就不会迷路。

下一篇:

《第二章:类加载机制与类生命周期 —— 双亲委派模型到底解决了什么问题?》

我们将深入分析:

  • 类加载器体系
  • 双亲委派机制
  • 类加载全过程
  • 类初始化时机
  • 面试经典题:为什么重写 String 类没用?