☕ 第一章 Java开发入门

1.4 Java程序的运行机制

📄 一句话概括

高级语言跑起来只有两种方式:编译型(整本翻译好再跑)和解释型(现场边翻边跑)。Java 的答案是两头都占:先用 javac 编译成字节码,再由 JVM 解释执行,跑得多的代码 JIT 直接编译成机器码缓存——这就是"一次编译、到处运行"背后的完整机制。

📝 本节是纯理论课,不用动手

但它是第一章最重要的一节:1.1 只说了结论(先编译再解释),这节把 JVM 拿到字节码之后发生的事拆开讲透。以后面试问"Java 是编译型还是解释型",答案就在这节。


1. 先看两大流派:编译型 vs 解释型

回忆计算机基础 05:CPU 只认机器码,高级语言必须翻译。翻译的时机有两种:

编译型(C / C++ / Go) 解释型(Python / JavaScript)
翻译方式 运行前整本翻好,产出机器码文件(.exe) 运行时现场翻译,读一句执行一句
速度 快(跑的全是母语机器码) 慢一些(边翻边跑)
跨平台 差——Windows 编译的 .exe,Mac 听不懂,换平台要重新编译 好——有解释器(翻译官)就能跑
发给朋友 发 .exe 发源码,对方得装解释器

一句话:编译型快但不跨平台,解释型跨平台但不快。 C 和 Python 各占一头。

2. Java 的答案:先编译,后解释

Java 不站队,它把两个动作串起来:

HelloWorld.java  --javac 整本编译-->  HelloWorld.class(字节码)
                                        ↓
                              各平台的 JVM 现场翻译执行

好处两头占:编译保证发布的是统一的字节码(跨平台);解释保证字节码能在任何平台跑起来。这就是 1.1 篇"混合派"的完整版说法。

💡 汇报里可以这么讲

问:"Java 是编译型还是解释型?" 答:"严格说都不是——它是先编译成字节码,再解释执行的混合机制。字节码面向 JVM 而不是 CPU,跨平台靠的是各平台各装一个 JVM。"


3. JVM 拿到字节码后的四站(本节核心)

java HelloWorld 敲下回车后,JVM 内部按顺序走四站:

① 类加载(ClassLoader) 把 HelloWorld.class 从硬盘搬进内存。按需加载——用到哪个类加载哪个,不是一口气全搬。

② 字节码校验(安检) 检查字节码有没有越界访问、伪造指针等危险操作。这一站是 Java "安全"名声的来源之一:字节码可能是从网上下载来的,先安检再执行。

③ 执行引擎:解释器 + JIT 双引擎

两个引擎配合:起步靠解释器(不冷场),热点靠 JIT(越跑越快)。这就是"Java 程序跑一段时间后性能会上升"的原因。

④ 运行时内存区 代码执行时的数据和对象都住在这,主要三个房间:

房间 住什么 谁管
堆(Heap) new 出来的对象(大头) GC 打扫
栈(Stack) 方法调用、局部变量(后进先出,像摞盘子) 方法结束自动清
方法区 类的信息、常量 类卸载时清
📝 程序计数器也是运行时区的一员

06 篇讲过 CPU 的"书签"(下一条指令在哪)。JVM 里每个线程都有自己的程序计数器,记着"当前线程执行到字节码第几条"——理论课的书签在这正式上岗。

4. GC:Java 帮你倒垃圾

C/C++ 程序员写代码要手动申请、手动释放内存,忘了释放就"内存泄漏"——程序越跑越肥,最后撑死。

Java 的解法:GC(垃圾回收器)自动盯着堆内存,发现哪个对象没人用了(没有任何引用指它),就定期收走。

⚠️ 有 GC ≠ 永远不泄漏

如果一个没用的对象还被某个集合攥着不放(比如一直往 List 里 add 却从不 remove),GC 认为"还有人用它",就不收——引用还在,垃圾就不算垃圾。所以 Java 也有内存泄漏,只是形式不同。这个话题学到集合(第四章后)会再碰。

📝 GC 是 JVM 在后台自动干的,不用调

System.gc() 只是"建议"GC 干活,它听不听随缘。初学阶段记住:写代码不用管内存回收,但要别攥着没用的对象不放。


5. 常见误区

⚠️ 误区 1:字节码就是机器码?

不是。机器码是给特定 CPU 看的(x86 和 ARM 都不一样);字节码是给 JVM 看的中间语,平台无关。把字节码直接喂给 CPU,它一脸懵。

⚠️ 误区 2:解释执行一定比编译执行慢得多?

放在 20 年前对。有了 JIT 后,热点代码会被编译成机器码缓存,跑得越久越接近纯编译型的速度——服务器上的 Java 程序长期运行时性能非常能打,这是它能守住企业后端的关键。

⚠️ 误区 3:装了 JDK 就能跑任何 .class?

跑 .class 只需要 JRE(JVM + 类库),不需要 javac 编译器。但开发者日常直接装 JDK(JDK ⊃ JRE ⊃ JVM,1.1 篇的结论)。

⚠️ 误区 4:类加载是一次性把所有类搬进内存?

不是,按需加载:main 里用到谁,加载谁。这也是为什么几 MB 的程序启动时只加载一部分类。

⚠️ 误区 5:Java 程序越跑越慢?

恰恰相反,JIT 让热点代码越跑越快。程序越跑越慢一般是内存泄漏(误区 4 的反例:攥着对象不放)或数据量变大,不是运行机制的锅。


6. 本节自测

❓ Q1:编译型和解释型的本质区别,一句话?

翻译的时机:编译型在运行前整本翻成机器码;解释型在运行时现场逐句翻。前者快但不跨平台,后者跨平台但慢。

❓ Q2:为什么说"字节码是 Java 跨平台的关键一环"?

它面向 JVM 而不是 CPU:所有平台上的字节码完全一样(一次编译),平台差异全被 JVM 吸收(各装各的翻译官)。少了字节码这一环,就得像 C 一样每个平台重新编译。

❓ Q3:JIT 是干什么的?为什么有了它 Java 不怕"解释执行慢"?

JIT 把被反复执行的热点字节码整段编译成机器码并缓存。首次走解释器(慢),跑热之后直接执行机器码(快),长期运行的程序整体性能接近编译型。

❓ Q4:一个对象"没用了"但 GC 不收,可能是什么原因?

还有引用指着它(比如一直往 List 里 add 不清理)。GC 的判断标准是"有没有人引用",不是"逻辑上还需要"。攥着不放就等于不给 GC 打扫的机会。

❓ Q5:`new` 出来的对象住在哪个内存区?谁负责回收它?

堆(Heap)。GC 负责在对象失去所有引用后回收;栈上的局部变量则随方法结束自动出栈,不走 GC。


7. 下一步学什么


8. 相关笔记

Java基础 #Java入门 #JVM #JIT #垃圾回收 #运行机制 #第一章