java运行原理_Java基础—Java运行原理

Java 运行原理(超级详细版)——从你写下一行代码到程序真正执行的全过程

这是 Java 基础中最核心、最底层、最常被问到的知识点之一。
无论你是准备面试、考研、蓝桥杯,还是真正想理解 Java 为什么“一次编写,到处运行”,都强烈建议把这一块吃透。

一、Java 程序从写出来到运行起来的完整流程(最清晰版)

你写的代码                  →  编译阶段                  →  运行阶段
HelloWorld.java     →→→   javac 编译     →→→   HelloWorld.class
                                                    ↓
                                             java 命令启动
                                                    ↓
                                             JVM(Java虚拟机)启动
                                                    ↓
                                        1. 类加载子系统(Class Loader)
                                        2. 运行时数据区(内存划分)
                                        3. 执行引擎(解释器 + JIT编译器)
                                        4. 本地方法接口(JNI)
                                                    ↓
                                            程序真正执行并输出结果

一句话总结

Java 是先编译后解释的混合执行语言,核心靠 JVM(Java Virtual Machine)实现跨平台。

二、详细拆解每一步(带图文流程)

1. 编写阶段:.java 源文件

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, Java运行原理!");
    }
}
  • 这是一个纯文本文件,人类可读。
  • 后缀必须是 .java
  • 文件名必须和 public 类名 完全一致(区分大小写)

2. 编译阶段:javac 把 .java → .class

命令:

javac HelloWorld.java

发生了什么?

  • 词法分析 → 把代码切成一个个 token(关键字、标识符、运算符、数字、字符串等)
  • 语法分析 → 构建语法树,检查语法是否正确(比如大括号是否匹配)
  • 语义分析 → 检查类型是否匹配、变量是否声明、方法是否被正确调用等
  • 生成字节码 → 把语法树转成 JVM 能理解的平台无关的字节码(.class 文件)

生成的 .class 文件长什么样?(用 javap 反编译看一下)

javap -c HelloWorld

你会看到类似下面的字节码(这是 JVM 真正能读懂的东西):

public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #7                  // String Hello, Java运行原理!
       5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

重要结论

  • .class 文件不是机器码,是中间码(字节码)
  • 字节码与平台无关(Windows、Mac、Linux 都能用同一份 .class)
  • 真正和硬件打交道的是 JVM,不是 .class

3. 运行阶段:java 命令启动 JVM

命令:

java HelloWorld
# 或(推荐写全类名)
java com.example.HelloWorld

这一步真正发生了什么?

JVM 启动后,会经历以下核心流程

启动 java.exe / java(Windows/Linux/Mac不同) → 加载 JVM 动态库 → 初始化 JVM → 执行 main 方法

JVM 内部核心工作分为三大块:

  1. 类加载子系统(Class Loader Subsystem)
  2. 运行时数据区(Runtime Data Area)
  3. 执行引擎(Execution Engine)

三、JVM 内部最详细的运行原理(重点!)

1. 类加载子系统(Class Loading)

把 .class 文件加载进内存,一共经历这几个阶段:

阶段做什么是否必须常见问题点
加载(Loading)通过类加载器把 .class 文件读进内存必须
链接(Linking)验证 + 准备 + 解析必须验证失败会报 VerifyError
验证(Verification)校验字节码格式、安全性、指令合法性
准备(Preparation)静态变量分配内存,赋默认值(0/null)注意:不是赋初始值
解析(Resolution)把符号引用转为直接引用(方法、字段、类)可选
初始化(Initialization)执行静态代码块静态变量的显式赋值必须最重要的阶段!

类初始化的触发时机(非常高频面试题):

  • new 一个对象
  • 访问静态变量 / 静态方法
  • 初始化子类(会先初始化父类)
  • main 方法所在类
  • 使用反射

2. 运行时数据区(内存划分)——最重要的一张图!

┌───────────────────────────────────────────────┐
│                  线程共享区                   │
│   ┌───────────────────────────────┐           │
│   │     方法区(Method Area)      │           │
│   │   (1.8+ 叫 元空间 Metaspace) │           │
│   │  存放:类信息、常量、静态变量   │           │
│   └───────────────────────────────┘           │
│                                               │
│   ┌───────────────────────────────┐           │
│   │       堆(Heap)              │           │
│   │  存放:几乎所有对象实例       │           │
│   └───────────────────────────────┘           │
└───────────────────────────────────────────────┘
┌───────────────────────────────────────────────┐
│                  线程私有区                   │
│   ┌───────────────────────────────┐           │
│   │       虚拟机栈(JVM Stack)   │           │
│   │  存放:栈帧(局部变量表、操作数栈)│       │
│   └───────────────────────────────┘           │
│                                               │
│   ┌───────────────────────────────┐           │
│   │     本地方法栈(Native Method Stack)│   │
│   └───────────────────────────────┘           │
│                                               │
│   ┌───────────────────────────────┐           │
│   │     程序计数器(PC Register) │           │
│   │  记录当前线程执行的字节码行号  │           │
│   └───────────────────────────────┘           │
└───────────────────────────────────────────────┘

最常考的几个区域对比

区域共享性存放内容可能抛出异常是否会GC
程序计数器线程私有字节码指令地址无(唯一不出错的区域)
虚拟机栈线程私有栈帧(局部变量、操作数栈等)StackOverflowError
本地方法栈线程私有native 方法的栈StackOverflowError
线程共享对象实例、数组OutOfMemoryError
方法区(元空间)线程共享类信息、常量、静态变量、JIT代码OutOfMemoryError是(部分)

3. 执行引擎(最核心的执行部分)

JVM 执行字节码有两种方式:

  1. 解释执行(Interpreter)
  • 一条一条解释字节码 → 翻译成机器码 → 执行
  • 启动快,但运行慢
  1. 即时编译(JIT Compiler) — HotSpot 的核心性能保障
  • 热点代码(经常执行的代码)编译成本地机器码
  • 编译后直接执行机器码,速度非常快
  • 热点判定依据:调用次数 / 循环回边次数达到阈值(-XX:CompileThreshold)

现代 JVM 真实执行方式

第一次执行 → 解释器解释执行 + 收集 profiling 信息
多次执行 → 达到阈值 → JIT 编译成机器码 → 放入 Code Cache
下次执行 → 直接执行机器码(极快)

四、总结:一句话版运行原理

你写的 .java 文件经过 javac 编译成平台无关的 .class 字节码,然后由 JVM 加载、链接、初始化,在运行时数据区分配内存,由解释器 + JIT编译器混合执行,最终把字节码转成当前机器能直接运行的本地机器码,从而实现“一次编写,到处运行”。

五、面试/考研常考 10 个追问问题(建议全部背熟)

  1. Java 为什么能跨平台?
  2. .class 文件是什么?能直接运行吗?
  3. JVM、JRE、JDK 分别是什么关系?
  4. 类加载的几个阶段分别做什么?
  5. 什么时候会触发类的初始化?
  6. 堆和栈的区别?分别存什么?
  7. 方法区(元空间)存什么?为什么 1.8 后改成了元空间?
  8. 解释执行和编译执行的区别?JVM 怎么结合使用的?
  9. 程序计数器有什么用?为什么线程私有?
  10. 运行一个 main 方法,JVM 都做了哪些事?

需要我继续深入讲解其中任意一个点吗?
比如:

  • 类加载的双亲委派模型
  • 字节码具体指令含义
  • JIT 编译的热点探测细节
  • 内存区域更详细的 GC 相关知识

随时告诉我,我们继续往下挖~

文章已创建 4357

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部