4. Hermes引擎性能特性
Hermes 是 Meta 为 React Native 专门设计的 JavaScript 引擎,它的目标不是追求极致的峰值执行速度,而是围绕移动端最敏感的指标——启动时间、内存占用和包体积——做系统性优化。理解 Hermes 的关键,是理解它把“编译”这件事从运行时前移到了构建时,并围绕这一决策重构了内存与启动路径。
字节码预编译:把解析成本挪出启动路径
传统 JSC 在运行时才把 JavaScript 源码解析、编译为可执行形式,这段工作全部落在用户点击图标之后的启动窗口里。Hermes 则使用 hermesc 编译器,在打包阶段就把 JS 编译成 Hermes 字节码(HBC),随 Bundle 一起下发。设备上运行时,引擎直接加载字节码,跳过了源码解析与大部分编译工作。
这意味着启动阶段省下的是“解析 + 编译”的 CPU 时间,而不是执行时间。对于中低端 Android 设备,这部分节省尤为明显。需要注意的是,字节码是版本相关的:Hermes 字节码格式与引擎版本绑定,升级 Hermes 后必须重新编译 Bundle,否则会出现字节码版本不匹配的加载失败。
内存管理:Hades 分代 GC 与更紧凑的堆
Hermes 早期使用 GenGC,后续引入 Hades 分代垃圾回收器。Hades 将堆分为新生代与老生代,多数短命对象在新生代被快速回收,避免频繁触发全堆扫描。相比 JSC 的 GC 策略,Hermes 的停顿更短、更可预测,这对动画和手势响应这类对卡顿敏感的场景很重要。
此外,Hermes 的字节码表示比源码更紧凑,字符串与对象布局也做了压缩,因此常驻内存和 Bundle 体积通常都低于 JSC。在内存紧张的设备上,这直接降低了被系统回收的概率。实践中可通过 HermesInternal.getRuntimeProperties() 观察引擎信息,结合 Android Studio Profiler 或 Xcode Instruments 对比堆增长曲线。
启动优化:TTI 与首屏可交互时间
启动优化是 Hermes 最直接的收益点。由于字节码预编译省去解析编译,加上 GC 停顿更短,从进程启动到 JS 首次执行、再到首屏可交互(TTI)的链路整体缩短。典型表现是白屏时间下降、首屏渲染更早触发。但要注意:Hermes 不解决业务代码本身的执行耗时,如果首屏逻辑里有大量同步计算或大对象构造,仍需单独优化。
与 JSC 的对比:取舍而非全面碾压
Hermes 与 JSC 并非简单的“谁更快”。Hermes 的优势集中在启动、内存和包体积;JSC 在长时间运行的纯计算场景中,凭借 JIT 可能获得更高的峰值吞吐。因此选择依据是应用形态:以启动速度和内存为生命线的移动 App,Hermes 通常更合适;若存在大量长时运行的数值计算,需要实测评估。
| 维度 | Hermes | JSC |
|---|---|---|
| 编译时机 | 构建时预编译为字节码 | 运行时解析编译 |
| 启动时间 | 通常更短 | 受源码解析影响 |
| 内存占用 | 堆更紧凑,GC 停顿短 | 相对更高 |
| 峰值执行 | 无 JIT,偏稳定 | JIT 可能更高 |
| 包体积 | 字节码更紧凑 | 源码体积较大 |
启用方式与验证
React Native 0.70 起 Hermes 默认开启。若需手动确认或调整,Android 在 android/app/build.gradle 中设置 hermesEnabled;iOS 在 Podfile 中通过 :hermes_enabled 控制。启用后应重新构建并清理缓存,避免旧字节码残留。
// android/app/build.gradle
project.ext.react = [
enableHermes: true
]
// ios/Podfile
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => true
)
HermesInternal.getRuntimeProperties(),若返回对象包含 bytecodeVersion 等字段,说明当前运行在 Hermes 上;若为 undefined,则仍在 JSC。综合来看,Hermes 的价值在于把可预测的成本前移到构建阶段,让运行时更轻、更稳。落地时应以真机冷启动时间、首屏 TTI 和内存峰值为判断依据,结合业务是否存在长时计算,决定是否启用以及是否需要针对首屏逻辑做进一步拆分。