移动开发中的Runtime:它是什么、运行时系统及如何工作

作者: IT Sectr 发布日期: 2026-05-17 阅读时间: 9 分钟

Runtime是一个软件层,负责管理移动应用代码的执行:分配内存、处理异常、启动垃圾回收并调度方法调用。没有运行时,任何应用都无法运行——它是编译代码与操作系统之间的中间层。根据Android Developer Documentation, 2025,运行时环境是平台的关键元素,决定了性能和兼容性。

要点

  • Runtime是执行移动应用字节码或机器码的软件环境。
  • ART(Android Runtime)使用AOT编译,并从Android 5.0起取代了Dalvik。
  • Objective-C Runtime在iOS中提供动态方法分发和消息传递。
  • JIT编译在应用运行期间直接将字节码编译为机器码。
  • ARM64 Runtime是执行针对64位ARM处理器优化代码的硬件层面。

移动开发中的Runtime是什么?

Runtime(运行时环境)是在程序启动后确保其执行的基础设施。在移动开发语境中,运行时包括类加载器、内存分配器、垃圾回收器、方法分发器和异常处理器。没有这个中间层,操作系统无法执行Dalvik字节码或Objective-C消息。

移动平台使用不同的运行时实现。Android使用ART(Android Runtime),采用混合AOT/JIT编译。iOS使用Objective-C Runtime——一种基于消息传递和SEL标识符的动态系统。两种方法解决同一个问题:在特定设备上以最大性能执行开发者的代码。

根据Google I/O 2024,Android Runtime在全球设备上每天处理超过100亿个方法。运行时的性能直接影响应用启动速度、动画流畅度和电池消耗。每次方法调用、每次内存分配和每个垃圾回收周期都经过运行时层。

Runtime系统:由哪些组件构成

运行时系统包括五个关键组件:类加载器、内存管理器、解释器或编译器、方法分发器和安全系统。每个组件在代码执行过程中都履行严格定义的功能。

类加载器与验证

当用户启动应用时,ClassLoader将DEX文件(Android)或Mach-O二进制文件(iOS)加载到内存中。在Android中,这一阶段包括字节码验证:运行时检查代码不包含不安全的指令、不越出数组边界并遵守类型。验证是防止恶意代码执行的关键安全步骤。

内存管理器与垃圾回收器

Memory Manager为对象分配和释放内存。在Android中,ART使用带分代收集的并发垃圾回收器:年轻对象检查更频繁,年老对象更少。Objective-C Runtime采用自动引用计数(ARC),编译器自动插入retain/release调用。

方法分发器与虚表

Method dispatcher决定调用哪个方法实现。在静态语言(Kotlin、Swift)中,分发通过vtable——虚方法表完成。在动态语言(Objective-C)中,消息经过objc_msgSend,它会在类及其超类中查找实现。结果缓存到method cache中以加速重复调用。

ART在Android上如何工作

Android Runtime(ART)是执行Android应用DEX字节码的虚拟机。ART在Android 5.0 Lollipop中取代了Dalvik,提供AOT编译:应用在安装期间一次性编译为机器码。这消除了每次启动时JIT编译的开销。

从Android 7.0 Nougat开始,ART采用混合方法。安装时仅对常用方法(hot methods)执行JIT编译,其余代码被解释。后台进程(profile-guided optimization)分析哪些方法被调用最频繁,并在设备空闲时将其AOT编译。这缩短了安装时间,同时保证了高性能。

ART还包括AOT compiler(dex2oat),将DEX文件转换为带ARM64机器码的ELF二进制文件。编译分三个优化级别:quicken(快速)、optimize(中等)和everything(完整)。默认情况下Android应用optimize,在编译速度和代码性能之间取得平衡。

kotlin
class RuntimeExample {
    fun measureExecutionTime() {
        val start = System.nanoTime()
        // 调用由ART编译的方法
        processData()
        val end = System.nanoTime()
        println("执行时间:${end - start} ns")
    }
}

在上面的示例中,System.nanoTime()是一个原生方法,其调用通过ART运行时调度到Linux内核。ART将Kotlin字节码转换为设备处理器执行的ARM64指令。这个过程对开发者不可见,但优化它是Android Platform团队的关键任务。

基于配置文件的优化(PGO)

基于配置文件的优化是ART收集方法使用配置文件的机制。文件profiles/.primary.prof包含AOT编译的hot方法列表。根据Android Performance Team,当配置文件收集完成后的几天使用后,PGO将应用启动速度提升15–30%。

开发者可以在其Gradle项目中启用baseline profiles。这些是手写注解,指示ART在安装后立即AOT编译哪些方法。Baseline profiles无需等待后台配置即可将首次启动缩短40%。

Objective-C Runtime在iOS上如何工作

Objective-C Runtime是确保Objective-C代码在iOS和macOS上执行的动态库。其核心是objc_msgSend函数,它实现消息传递:对象不是直接调用方法,而是发送带选择器的消息,运行时确定应执行哪个实现。

每个Objective-C对象都包含指向类的isa指针,类则维护dispatch table(分发表),将选择器(SEL)映射到实现(IMP)。当调用方法时,objc_msgSend沿链移动:类→超类→NSObject,直到找到IMP。如果找不到实现,运行时调用转发机制,它可以拦截消息或生成异常。

Objective-C Runtime还支持method swizzling——在运行期间替换现有选择器的IMP。这是AOP库和监控工具中使用的强大机制,但由于影响整个应用而需要谨慎。

objective-c
@interface RuntimeDemo : NSObject
- (void)printClassInfo;
@end

@implementation RuntimeDemo
- (void)printClassInfo {
    // objc_getClass — runtime函数
    Class cls = objc_getClass("RuntimeDemo");
    unsigned int count;
    Method *methods = class_copyMethodList(cls, &count);
    NSLog("方法数量:%d", count);
}
@end

代码演示了对Objective-C Runtime API的直接访问:objc_getClass按名称获取类对象,class_copyMethodList提取所有方法的列表。这是运行中的反射——在运行期间访问类的元数据。这种方法在XCTest中用于动态注册测试。

isa指针与tagged pointers

isa pointer是指向对象类的指针,存储在每个对象的前8个字节中。从iOS 12开始,Apple引入了isa-swizzling以优化:isa的低位对对象状态的附加信息进行编码。Tagged pointers是另一种优化,其中最多60位的值(NSNumber、NSDate)直接存储在指针中,而无需在堆中分配对象。这将内存管理器的负载降低30%。

JIT与AOT编译:方法比较

JIT(Just-In-Time)和AOT(Ahead-Of-Time)是将字节码编译为机器码的两种方法。JIT在应用运行期间编译代码,分析热点并即时优化。AOT预先编译所有代码——在应用安装时或由开发者完成。

特征JITAOT
编译时间运行期间安装/构建时
APK/IPA大小更小(仅字节码)更大(机器码)
启动速度更低(需要编译)更高(代码可立即执行)
针对设备的优化是(自适应)有限(通用)
RAM消耗更高(内存中的编译器)更低

ART的混合方法(Android 7+)被认为是最优的:应用对很少调用的方法使用解释器,对hot方法使用JIT,对来自基于配置文件优化的方法使用AOT。相反,iOS通过LLVM使用严格的AOT:Swift和Objective-C在Xcode的构建阶段编译为机器码。

根据Apple Developer Documentation, 2024,Swift运行时为应用大小增加约15 MB。Flutter使用自己的Dart VM,其中JIT编译在debug模式下用于热重载,AOT在release模式下用于最大性能。React Native使用Hermes——带AOT编译的JavaScript引擎,将启动时间缩短50%。

ARM64 Runtime与机器码

ARM64 Runtime是机器码与设备处理器交互的层面。大多数现代移动设备运行在ARM64(aarch64)处理器上。运行时将字节码或原生调用转换为CPU执行的ARM64指令。

运行时使用的关键ARM64寄存器:x0–x7(函数参数)、x8(间接结果)、x30(返回地址)、sp(堆栈指针)、fp(帧指针)。ART生成的代码遵守ARM64 Procedure Call Standard:所有方法调用都经过处理器架构定义的协议。

理解ARM64 ABI对性能优化很重要:内联缓存、分支预测和内存中的代码对齐直接影响运行时的运行速度。分析工具(Android Studio Profiler、Instruments)显示代码的哪些部分在运行时中花费最多时间——正是优化这些部分能带来最大提升。

cpp
// ART生成的ARM64汇编示例
// 带两个参数的方法调用

mov    x0, x23            // self (this)
mov    x1, x24            // param1
mov    x2, x25            // param2
bl     methodEntryPoint   // 通过runtime调用
str    x0, [sp, #8]      // 保存结果

在此示例中,ARM64指令mov将参数传输到x0–x2寄存器,bl调用方法的入口点,str保存返回值。运行时为每次方法调用生成此类指令,通过devirtualization和inlining优化序列。

运行时对性能的影响

Runtime overhead是动态分发的不可避免代价。每次通过运行时的方法调用都需要:在dispatch table中查找实现、检查类型、调用IMP并返回结果。测量表明,运行时在Objective-C中每次调用增加10–50 ns,在ART中增加5–20 ns。

为减少开销,开发者使用monomorphic inlining(ART)和method caching(Objective-C)。Kotlin/Native和Swift直接编译为ARM64,完全消除运行时层,但失去动态能力——反射、swizzling、类动态加载。

常见问题

Runtime与SDK有什么区别?

SDK(软件开发工具包)是开发应用的成套工具(编译器、库、实用程序)。Runtime是已经开发好的应用在设备上执行的环境。开发者需要SDK,用户需要runtime。

可以在移动应用中替换Runtime吗?

不能——runtime是操作系统的一部分,用户无法替换。ART内置于Android Framework,Objective-C Runtime内置于iOS。开发者可以选择语言(无runtime的Kotlin/Native)或使用Flutter中的Dart VM等虚拟机。

Runtime影响电池消耗吗?

是的,runtime影响能量消耗。ART中的垃圾回收和Swift运行时使用CPU,增加电量消耗。并发GC和iOS中的tagged pointers等优化将runtime对电池的影响降低20–30%。

什么是runtime error,如何捕获?

Runtime error是运行期间发生的错误:空指针异常、索引越界、除零。与编译时错误不同,这些错误在构建时无法发现。通过try-catch块或崩溃报告(Firebase Crashlytics、Sentry)捕获。

Swift runtime与Objective-C Runtime有何不同?

Swift runtime比Objective-C更轻量:默认不支持动态分发,使用无堆分配的值类型(struct),且没有消息转发。Swift方法如果没有用@objc dynamic标记,则直接通过vtable调用。这在基准测试中提供高达5倍的加速。

总结

  • Runtime是管理内存、方法和代码安全的运行时环境。
  • ART(Android)使用带基于配置文件优化的JIT/AOT混合方案,以获得最佳性能。
  • Objective-C Runtime基于通过objc_msgSend和dispatch table的消息传递。
  • JIT即时编译代码并适应设备,AOT为快速启动提前编译。
  • ARM64 Runtime是在现代处理器上执行机器码的硬件层。
  • Runtime overhead为每次方法调用5–50 ns,并通过内联和缓存最小化。
  • 理解runtime对于性能优化、调试和应用架构选择是必要的。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读