【碎片八股文 #005】Hermes 引擎和 JSC 有什么区别?
一、面试题原文
面试官: React Native 为什么要推出 Hermes 引擎?它和 JavaScriptCore 有什么区别?
候选人: Hermes 好像更快吧……具体为什么不太清楚。
面试官心里想: 能说出字节码预编译、AOT、启动速度、内存优化就算过关了。
二、常见误答
很多人只知道"Hermes 性能更好",但说不清楚:
- Hermes 的"快"体现在哪里?
- 为什么 JSC 不够快?
- Hermes 是如何做到更快的?
这些都是面试官会追问的点。
三、正确理解
什么是 JavaScript 引擎?
JavaScript 引擎负责 解析和执行 JS 代码。
React Native 支持两种主流引擎:
| 引擎 | 来源 | 使用场景 |
|---|---|---|
| JavaScriptCore (JSC) | WebKit 项目(Apple) | RN 默认引擎(iOS 系统内置) |
| Hermes | Facebook 开源 | RN 官方推荐(Android 优化显著) |
Hermes 的核心设计目标
Hermes 是 Facebook 专门为 React Native 优化的引擎,三大目标:
- 减少启动时间(App 打开更快)
- 降低内存占用(适合低端设备)
- 减少包体积(APK/IPA 更小)
四、核心区别对比
1. 编译方式:JIT vs AOT
| 特性 | JSC | Hermes |
|---|---|---|
| 编译时机 | 运行时 JIT 编译 | 构建时 AOT 编译 |
| 代码格式 | JS 源码 | 字节码(.hbc) |
| 启动阶段 | 边解析边执行 | 直接执行字节码 |
JSC 的 JIT(Just-In-Time)编译:
应用启动 → 加载 JS 源码 → 解析成字节码 → JIT 编译 → 执行
↑ ↑
耗时操作 耗时操作
Hermes 的 AOT(Ahead-Of-Time)编译:
构建阶段:JS 源码 → 预编译成字节码 → 打包到 APK
应用启动:加载字节码 → 直接执行
↑
超快!
关键差异: Hermes 把编译工作提前到构建阶段,启动时直接执行字节码。
2. 启动性能对比
测试场景: 启动一个中型 RN 应用(包含 React Navigation)
| 指标 | JSC | Hermes | 提升 |
|---|---|---|---|
| 首屏时间 | ~4.2s | ~2.1s | 50% |
| JS 解析耗时 | ~1.8s | ~0.3s | 83% |
| 内存占用 | ~60MB | ~35MB | 42% |
数据来源: Facebook 官方测试(Android 中端机型)
3. 内存管理策略
JSC:
- 采用 分代垃圾回收(Generational GC)
- 内存占用较高,适合性能强的设备
Hermes:
- 采用 增量式垃圾回收(Incremental GC)
- 分片回收,减少卡顿
- 专门优化低端设备的内存使用
4. 包体积影响
JSC:
- JS bundle 是源码,体积较大
- Android 需要额外集成 JSC 库(~1.5MB)
Hermes:
- 字节码比源码更紧凑
- 引擎本身体积更小(~1.2MB)
- 整体包体积减少 15-20%
五、图解核心原理
JSC 的运行流程
┌─────────────────────────────────────────────────┐
│ 应用启动阶段(运行时) │
│ │
│ 加载 index.js │
│ ↓ │
│ 词法分析 + 语法分析(慢) │
│ ↓ │
│ 生成字节码 │
│ ↓ │
│ JIT 编译热点代码(慢) │
│ ↓ │
│ 执行 JavaScript │
└─────────────────────────────────────────────────┘
Hermes 的运行流程
┌─────────────────────────────────────────────────┐
│ 构建阶段(打包时) │
│ │
│ index.js → Hermes 编译器 → index.hbc(字节码) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 应用启动阶段(运行时) │
│ │
│ 加载 index.hbc 字节码 │
│ ↓ │
│ 直接执行(超快!) │
└─────────────────────────────────────────────────┘
关键点: 编译工作前移,启动时零编译开销。
六、延伸提问
1. Hermes 有什么缺点?
缺点 1:不支持 eval() 和 new Function()
因为字节码是预编译的,运行时无法动态生成代码:
// 这段代码在 Hermes 中会报错
eval('console.log("hello")'); //不支持
new Function('x', 'return x * 2')(5); //不支持
解决方案: 避免使用动态代码生成,改用静态方法。
缺点 2:调试体验稍差
由于是字节码,调试时看不到原始 JS 代码(需要 sourcemap 映射)。
缺点 3:iOS 上提升不明显
iOS 系统自带 JSC 且高度优化,Hermes 的优势主要在 Android。
2. 如何在 RN 项目中启用 Hermes?
Android(默认启用):
// android/app/build.gradle
project.ext.react = [
enableHermes: true // 默认就是 true
]
iOS(需要手动启用):
# ios/Podfile
use_react_native!(
:hermes_enabled => true # 启用 Hermes
)
然后重新安装 Pods:
cd ios && pod install
3. Hermes 和 V8 引擎有什么区别?
| 特性 | Hermes | V8 |
|---|---|---|
| 设计目标 | 移动端优化 | 高性能通用引擎 |
| 启动速度 | 快(AOT) | 慢(JIT) |
| 运行时性能 | 中等 | 极快(优化编译) |
| 内存占用 | 低 | 高 |
| 包体积 | 小 | 大 |
结论: Hermes 牺牲了部分运行时性能,换取启动速度和内存优势。
4. 为什么 Hermes 不做 JIT 优化?
JIT 的问题:
- 需要在运行时编译,消耗 CPU 和内存
- 移动设备性能有限,JIT 编译反而拖慢启动
- iOS 系统不允许动态生成可执行代码(安全限制)
Hermes 的选择: 用 AOT 预编译 + 轻量级解释器,更适合移动端。
七、记忆口诀
"JSC 运行时编译慢,Hermes 提前编字节;启动快、内存省,移动端的好伙伴。"
八、碎片笔记
核心关键词: Hermes、JSC、AOT、JIT、字节码、启动速度、内存优化
重点记忆:
- JSC 是运行时 JIT 编译,Hermes 是构建时 AOT 编译
- Hermes 启动速度提升 50%,内存降低 40%
- Hermes 不支持 eval() 和 new Function()
- iOS 上 Hermes 提升不如 Android 明显
实际应用:
- 新 RN 项目优先使用 Hermes(Android 默认启用)
- 避免在代码中使用动态代码生成(eval、new Function)
- 如果有复杂计算逻辑,Hermes 的解释执行可能不如 JSC 的 JIT 快
- 调试时记得配置 sourcemap,否则看不到原始代码
今天的碎片,帮你面试少挂一次。
下一篇预告: 【碎片八股文 #006】RunLoop 为什么能让主线程不退出?