---
title: "底层深入-什么是 运行时"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/what-is-runtime/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/05-游戏图形与运行时/WebGL/底层深入-什么是 运行时.md"
content_hash: 6a673c31ea74028c4276972ebce552fc4ad645671bea8892bd37989524b055fb
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# 底层深入-什么是 运行时

﻿# 底层深入-什么是 运行时



## 什么叫“运行时”？

1. **语言规范负责说“语义”**——`for` 如何展开、异常如何传播等。
2. **编译器/字节码生成器负责把源代码翻译成中间形式**（JVM 字节码、CIL、Python bytecode、JS bytecode…）。
3. **运行时** = **进程内那段常驻代码 + 数据区**，在程序 *执行期* 解析/执行这些中间形式，并向宿主操作系统或宿主环境（Host）做系统调用。

   * 关键职责：

     * **装载**（Class Loader / Metadata Loader / `PyImport` / Script Loader）
     * **执行引擎**（解释器循环或 JIT/AOT 生成机器码）
     * **内存管理**（堆、栈、垃圾回收或引用计数）
     * **运行时库**（字符串、集合、反射、I/O）
     * **原生互操作**（JNI、P/Invoke、C-API、Web IDL…）

---
## 几种运行时
### Java 运行时：JVM

#### 核心抽象

* **ClassLoader 子系统**：按委托模型链式查找 `.class` → 填充方法区元数据。([GeeksforGeeks][1])
* **Runtime Data Area**：线程私有 *栈*、程序计数器；共享 *堆*、方法区、运行时常量池。([FreeCodeCamp][2])
* **Execution Engine**：包含解释器与 C2/C1 JIT；遇到热点方法即从字节码生成机器码。
* **GC**：G1/ZGC/Serial GC 等通过写屏障维护分代堆。

#### 伪代码：类加载 + 解释循环

```java
byte[] bytes = readClassFile(name);
Class c = defineClass(bytes);        // → 方法区注册

while (pc < bytecode.length) {       // 解释器
    switch(bytecode[pc++]) {
        case IADD: stack.push(stack.pop() + stack.pop()); break;
        case INVOKEVIRTUAL:
            Method m = cp.resolveMethod(index);
            frameStack.push(new Frame(m));
            break;
        ...
    }
}
```

---

### Mono 运行时（.NET CLR 的开源实现）

* **Metadata Loader** 读取 ECMA-335 元数据表；同 JVM 的常量池。
* **JIT/Interpreter/AOT** 三种模式可切换（iOS 可静态 AOT）。([DeepWiki][3], [Mono Project][4])
* **GC**：SGen（分代、并行压缩）或 `libgc`；托管对象由 write barrier 追踪引用。
* **P/Invoke**：自动生成“托管→本地”桩代码让 C# 调 C 库。

伪代码（极简 JIT）

```c
IR *ir = mono_method_to_ir(method);
Code *code = mono_codegen(ir);   // x64/arm64 等后端
void* entry = jit_alloc(code->size);
memcpy(entry, code->bytes, code->size);
method->entry = entry;
```

---

### Python 运行时（CPython）

* **PyInterpreterState / PyThreadState** 结构体保存全局与线程局部变量。([DeepWiki][5])
* **字节码虚拟机**：名为 *ceval loop*，对每条 `op` 做栈式运算。
* **内存模型**：PyObject 头部引用计数字段，循环引用靠分代 GC 回收。

伪代码：核心执行循环

```c
for (;;) {
    int opcode = NEXTOP();
    switch(opcode) {
        case LOAD_FAST:  PUSH(fastlocals[i]);           break;
        case CALL_FUNCTION:
            PyObject *func = POP();
            PyObject *args = POP();
            PUSH(PyObject_Call(func, args, NULL));      break;
        ...
    }
}
```

---

### ECMAScript 运行时 + Host
**ECMAScript（简称 ES）是一部由 _Ecma International_ 制定的脚本语言规范，文件编号 ECMA-262**。它定义了语言的语法、类型系统、内建对象、错误模型、并发抽象（Promise Jobs）等核心语义，用来保证不同实现之间可以运行同一段脚本代码。

> - **1995 年** Brendan Eich 在 Netscape 浏览器中实现了早期的 _LiveScript/JavaScript_。
> - 为了避免与 Sun 的“Java”商标纠纷，1996 年 Netscape 把这门语言送交标准机构 _ECMA_，双方同意用 **“ECMAScript” 作为标准名称**，浏览器实现继续对外叫 “JavaScript”。

#### 语言引擎

* 解析 → 生成 AST/IR → 点燃 JIT（Ignition/TurboFan、LLInt/DFG JIT…）。
* 垃圾回收：Oilpan（V8）、Mark-Sweep/Mark-Compact。

#### Host（浏览器 / Node / WeChat Game Runtime）

* 提供 **事件循环队列**、DOM、`setTimeout`、`wx.request` …。
* 规范把这些称作 **Host APIs**；语言层只认 `Job Queue` 与 `Host*` 钩子。([ECMAScript 2024 Language Specification][6], [Byby][7])

伪代码：`HostPromiseRejectionTracker`

```js
function HostPromiseRejectionTracker(p, op) {
  if (op === 'reject') {
    queueMacroTask(() => {
      if (!p.handled) dispatch('unhandledrejection', {reason: p.result});
    });
  } else if (op === 'handle') {
    p.handled = true;
  }
}
```

微信小游戏 Runtime 在 C++ / Java 层实现 `queueMacroTask`，把任务压到主线程尾，再经 JSCore/V8 调度（与浏览器概念相同，只是没有 DOM）。

#### 运行时实现
**V8** 由 Google 于 2008 年为 Chrome 开发，名字源于 **“V-8” 引擎**，寓意高性能。
- 它同时实现 **ECMAScript** 和 **WebAssembly**，被 Node.js、Deno、Electron、微信小游戏（Android）等运行时复用，成为最广泛部署的 ES 引擎之一。

**主流 JavaScript 引擎与来源**：

| 引擎                 | 主要维护方           | 首次发布 | 名称由来 / 定位                                       |
| ------------------ | --------------- | ---- | ----------------------------------------------- |
| **V8**             | Google          | 2008 | 命名取自 *V-8 汽车发动机*，强调“高性能”                        |
| **SpiderMonkey**   | Mozilla         | 1996 | Brendan Eich 首个 JS 实现，被火狐延续至今                   |
| **JavaScriptCore** | Apple / WebKit  | 2002 | Safari 内置；别名 Nitro／SquirrelFish；同样实现 ECMAScript |
| **Hermes**         | Meta            | 2019 | React Native 专用，面向移动端低内存场景                      |
| **QuickJS**        | Fabrice Bellard | 2019 | 超小型、支持最新 ES 特性，常嵌入 IoT                          |

## 把四种运行时放在一张脑图里

* **装载器**：ClassLoader / .NET MetadataLoader / `PyImport` / ScriptLoader
* **执行引擎**：解释器 & JIT (+AOT)
* **内存管理**：GC（分代、压缩、并行）、引用计数
* **宿主接口**：JNI / P-Invoke / C-API / `window.*` `wx.*`

> **运行时就是“让字节码真正跑起来”的那整套代码与数据区**；
> 编写它意味着：写 Parser/JIT/GC、做平台抽象层、暴露宿主 API，并持续调优内存与性能。理解了这些通用部件，再看任何语言的“runtime”文档就能迅速对号入座。

[1]: https://www.geeksforgeeks.org/jvm-works-jvm-architecture/?utm_source=chatgpt.com "How JVM Works - JVM Architecture - GeeksforGeeks"
[2]: https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/?utm_source=chatgpt.com "JVM Tutorial - Java Virtual Machine Architecture Explained for Beginners"
[3]: https://deepwiki.com/dotnet/runtime/7-mono-runtime?utm_source=chatgpt.com "Mono Runtime | dotnet/runtime | DeepWiki"
[4]: https://www.mono-project.com/docs/advanced/runtime/?utm_source=chatgpt.com "The Mono Runtime"
[5]: https://deepwiki.com/python/cpython/4-runtime-and-thread-management?utm_source=chatgpt.com "Runtime and Thread Management | python/cpython | DeepWiki"
[6]: https://262.ecma-international.org/?utm_source=chatgpt.com "ECMAScript® 2024 Language Specification"
[7]: https://byby.dev/js-host-environments?utm_source=chatgpt.com "JavaScript Host Environments - byby.dev"


## 拓展延申：原生互操作的实现
 * **原生互操作**（JNI、P/Invoke、C-API、Web IDL…）

以JNI和P/Invoke为例进行说明：

| 步骤         | JNI                                                                                          | P/Invoke                                                                                                 |
| ---------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| ① 托管端声明    | `native` 关键字写进 .class → ClassFile 标志 `ACC_NATIVE`                                            | `extern` + `[DllImport]` 写进 IL → 方法标志 `pinvokeimpl`                                                      |
| ② 运行时解析    | `System.loadLibrary()` 触发 **动态装载**；JVM 用 `dlsym/GetProcAddress` 找 `Java_pkg_Class_meth` 或注册表 | CLR 在 **JIT** 时查看元数据→调用 `LoadLibrary/dlopen` & `GetProcAddress`                                          |
| ③ 桩代码生成    | JVM 按平台 ABI 生成 _native stub_：• 入栈 `JNIEnv*` + `jobject` + 参数• 建立 **JNI本地帧** 屏蔽 GC            | CLR 生成 **IL Stub**（.NET 7 起可用 **Source Generator**，提前产出 C#→CIL）：• 参数封送（marshal）• 线程进入 _Preemptive GC_ 模式 |
| ④ 跨界调用     | C 函数里通过 `JNIEnv*` 表调用 JVM 服务；返回时释放本地引用                                                       | C 函数直接按 C 调用约定返回；IL Stub 再把结果封送回托管对象                                                                     |
| ⑤ 异常/GC 协作 | C 抛异常 ⇒ `(*env)->ThrowNew` 映射到 Java；GC 不会移动 JNI 本地引用                                         | C 侧报错通过 `SetLastError`，托管层 `[DllImport(SetLastError=true)]` 捕获；GC 期间 stub 自动 pin/unpin                   |

### 0. 设计总览

```text
┌─────────┐      ┌───────────────┐      ┌─────────────┐
│  元数据 │──┐──▶│  延迟解析桩   │──┐──▶│   真正桩    │──▶ 原生函数
└─────────┘  │   └───────────────┘  │   └─────────────┘
             │                      │
             ▼                      ▼
  类加载阶段处理 ACC_NATIVE   首次调用时 patch entry
```

> **核心链路：** *标记 native → 延迟解析 → 找符号 → 生成/缓存桩 → 线程 & GC 保护 → 参数封送 → 调原生*

---

### 1. 元数据加载阶段

| 任务               | JNI (HotSpot)             | P Invoke (CoreCLR)                   | 交付物                                   |
| ---------------- | ------------------------- | ------------------------------------ | ------------------------------------- |
| 解析“这是 native 方法” | `ACC_NATIVE` 写进 `Method*` | `pinvokeimpl` 写进 `MethodDesc`        | 修改 **Class/Type Loader**，支持 native 标志 |
| 保存额外属性           | 无（符号名通过约定推算）              | `DllImport` 属性：库名、调用约定、SetLastError… | 在 **运行时元数据表** 中新增字段                   |

---

### 2. 符号解析 & 动态库管理

| 步骤         | JNI                                                      | P Invoke                       | 要点                              |
| ---------- | -------------------------------------------------------- | ------------------------------ | ------------------------------- |
| 触发时机       | `System.loadLibrary()` 或第一次调用                            | JIT 初次触发 P Invoke              | 都选 **懒加载**；失败时抛高层异常             |
| 动态装载       | `dlopen`/`LoadLibrary`                                   | 同上                             | 维护 **NativeLibraryCache**，库句柄唯一 |
| 符号名规则      | `Java_{pkg}_{Class}_{method}__{sig}` 替换 `/ → _`,`_ → _1` | 函数名直接写在 `DllImport`            | 提供 **NameManglingUtil**（可配置）    |
| Patch 回调地址 | `Method*::set_native_function`                           | `MethodDesc::SetNDirectTarget` | 确保线程安全，双检 + 锁或 CAS              |

---

### 3. 桩（Stub）生成器

#### 3.1 生成策略

| 维度   | JNI                                 | P Invoke                     | 建议                                          |
| ---- | ----------------------------------- | ---------------------------- | ------------------------------------------- |
| 生成时机 | 每个 native 方法一次（或 AOT 缓存）            | JIT 一次，或 .NET 7 起 Source Gen | 先做 **即时生成**，后期再做 AOT                        |
| 目标   | 按平台 ABI & GC 协议包装                   | 生成 IL，再由 JIT 编译              | 统一走 **StubEmitter**：C++ 模板 + MacroAssembler |
| 性能阶梯 | 普通 JNI → Fast JNI → Critical Native | 普通 P Invoke → Stub 内联 → 直接调用 | 暴露“Fast 调用”注解供高级用户                          |

#### 3.2 桩内容模板（伪代码）

```assembly
; prologue
push rbp
mov  rbp, rsp
sub  rsp, local_size
call ThreadState_EnterNative   ; 切换到 native / Preemptive GC

; 参数封送
mov rdx, [rbp+arg0]            ; e.g. jint → int32
; ...

; 调用原生符号
call [resolved_addr]

; 返回值封送 / 异常映射
call ThreadState_LeaveNative
mov  rsp, rbp
pop  rbp
ret
```

---

### 4. 封送 (Marshalling) 引擎

| 类型类别 | JNI API                              | P Invoke Attribute / API                           | 关键代码                 |
| ---- | ------------------------------------ | -------------------------------------------------- | -------------------- |
| 基元   | 直接复制                                 | 直接复制                                               | 无                    |
| 字符串  | `GetStringUTFChars` / `NewStringUTF` | `[MarshalAs(UnmanagedType.LPUTF8Str)]`             | **StringMarshaller** |
| 数组   | `GetArrayElements`        | `fixed` / `GCHandle` pin                           | **PinnedArrayPool**  |
| 结构体  | 手写 `GetFieldID` + byte copy          | `StructLayout(LayoutKind.Sequential)`              | 解析 C# 布局元数据生成拷贝表     |
| 异常   | `ThrowNew`, `ExceptionCheck`         | `SetLastError=true`, `Marshal.GetLastWin32Error()` | **ErrorMapper**      |

---

### 5. 线程 & GC 协作

| 事项     | JNI                              | P Invoke                     | 实现要求                               |
| ------ | -------------------------------- | ---------------------------- | ---------------------------------- |
| 线程附加   | `AttachCurrentThread` / `Detach` | 自动                           | 维护 **ThreadLocal JNIEnv / GCMode** |
| GC 安全点 | Pin 本地引用；本地帧                     | Preemptive vs Cooperative    | 在桩的 prologue / epilogue 标记状态       |
| 反向调用   | `Call<type>Method`               | Delegate → 函数指针 (`GCHandle`) | 生成 **Reverse Stub**，确保再次切换 GC 模式   |
