高级 #compiler#jit#runtime

JIT(即时编译)

JIT(Just-In-Time Compilation)在程序运行时把热点代码编译成机器码——结合了解释器的灵活性和编译器的性能。Java JVM、V8(JavaScript)、LuaJIT 都使用 JIT

⚡ “先跑起来,慢了再优化”

编译器一次性编译完再运行——启动慢但执行快。 解释器逐行运行——启动快但执行慢。

JIT(Just-In-Time Compilation,即时编译)结合两者的优点

先解释执行 → 快速启动

监控哪些代码运行最频繁(热点代码)

热点代码 → 编译成机器码 → 加速执行

不再热了的代码 → 丢弃编译结果 → 回到解释执行

🏪 类比:做饭的两种方式

传统编译(AOT) = 你提前做好一整周的便当放冰箱——吃的时候微波炉一热就行(执行快),但准备时间长(编译慢)。

解释执行 = 每顿现做——想吃什么立刻做(启动快),但每次都要洗菜切菜炒菜(执行慢)。

JIT = 平时吃速食(解释执行),但发现你每周三都吃鱼香肉丝——提前做好一批鱼香肉丝冻着,周三直接热来吃(热点编译)。


🔥 热点检测——“值得编译吗”

JIT 不是把所有代码都编译——只编译”热点(Hot Spot)”。

// JVM 的热点检测
int sum = 0;
for (int i = 0; i < 1000000; i++) {  // 这个循环执行了 100 万次
    sum += arr[i];                      // → 被标记为"热点"
}                                       // → JIT 把这个循环编译成机器码
                                        // → 后续执行直接用编译后的机器码

JVM 使用计数器阈值

方法调用计数器:一个方法被调用超过 N 次 → 编译
回边计数器:循环体被执行超过 M 次 → 编译

阈值可调:-XX:CompileThreshold=10000

📊 编译等级——从”快速”到”极致”

JVM 的 JIT 有多个编译等级(-client 到 -server):

等级 0:解释执行(启动最快)
等级 1:简单 C1 编译(快速优化,不花太多时间)
等级 2:C1 编译 + 更多 profiling
等级 3:C1 编译 + 全 profiling
等级 4:C2 编译(极致优化——花更多时间编译,生成更快的代码)

随着运行时间推移——代码从等级 0 逐步上升到等级 4
如果等级 4 的代码不再频繁执行——回退到解释模式
# JIT 编译的思想在 Python 中的体现——PyPy
# CPython(标准 Python):纯解释器 → 慢
# PyPy:包含 JIT 编译器 → 比 CPython 快 4-10 倍

def sum_array(n):
    total = 0
    for i in range(n):
        total += i
    return total

# CPython:每次运行都逐行解释 → 慢
# PyPy:第一次解释,发现 total += i 是热点
#       → 编译成机器码 → 后续直接跑机器码 → 快

🏢 使用 JIT 的语言

语言/平台JIT 实现特点
Java(JVM)C1(Client)、C2(Server)、Graal分层编译,自适应优化
JavaScript(V8)TurboFan + Ignition解释器+JIT,快速启动
C#(.NET)RyuJIT兼顾启动速度和峰值性能
PyPy(Python)内建 JIT比 CPython 快 4-10 倍
LuaJIT高度优化的 JIT号称最快的动态语言实现

📝 小结

概念一句话
JIT(即时编译)运行时把热点代码编译成机器码——解释器+编译器的混合
热点检测监控方法调用次数/循环次数——达到阈值就编译
分层编译从解释→轻度编译→深度优化编译——渐进式
去优化(Deoptimization)编译后的代码不再适用→回退到解释模式
PyPy vs CPythonJIT 是 PyPy 比 CPython 快几倍的核心原因

为什么先学这个? JIT 是编译器技术的”运行时应用”。现代语言的性能很大程度上取决于 JIT 的质量——理解了 JIT,你就理解了”为什么 Java 越来越快”和”为什么 Python 可以更快”。