casino365sport365

Android ART知多少?

Android ART知多少?

Android 虚拟机 ART(Android Runtime)是 Android 平台上的应用程序运行时环境,用于执行应用程序的字节码。ART 自 Android 5.0(Lollipop)开始取代了 Dalvik,成为 Android 的默认运行时环境。本文将从以下几个方面详细系统地介绍 ART,并结合部分源码来探讨其实现原理:

ART 简介ART 与 Dalvik 的对比ART 的架构与核心模块关键技术

Ahead-of-Time 编译 (AOT)Just-In-Time 编译 (JIT)垃圾回收 (GC)内存管理与优化 源码剖析性能优化的设计理念总结

1. ART 简介

ART 是 Android 平台上的一种高效、稳定的运行时,专为移动设备优化。与 Dalvik 的解释执行不同,ART 结合了提前编译和即时编译,显著提升了应用性能和响应速度,同时降低了内存消耗。

主要特点:

高性能:通过 AOT 和 JIT 提高应用运行效率。优化内存使用:改进 GC 算法,减少暂停时间。改进调试工具:支持更强大的调试和分析功能,如 TraceView 和 HPROF。兼容性强:支持运行旧的 Dalvik 字节码。

2. ART 与 Dalvik 的对比

特性ARTDalvik编译模式AOT + JIT解释执行 + JIT初次启动时间较长较短应用运行性能高中等内存消耗较低较高垃圾回收并发、低暂停的 GC停止-复制的 GC支持工具TraceView、Perfetto 等基本调试工具

3. ART 的架构与核心模块

ART 的架构分为以下几个核心部分:

ClassLoader: 负责加载字节码。Compiler (AOT/JIT): 提供提前和即时编译功能。Garbage Collector (GC): 负责内存分配与回收。Runtime: 提供运行时环境,管理线程、类、方法调用等。Debugger 和 Profiler: 提供调试和性能分析工具。 下面是架构图示意:

4. 关键技术

4.1 Ahead-of-Time 编译 (AOT)

Android Runtime (ART) 的 Ahead-of-Time (AOT) 编译 是一种在应用安装时,将字节码(Bytecode)直接编译为机器码(Native Code)的技术,从而提升应用运行时的性能。下面详细说明:

背景与原理

Dalvik VM 的问题在 Android ART 之前,Android 使用的是 Dalvik 虚拟机,它主要依赖 Just-in-Time (JIT) 编译技术。在应用运行时,字节码被即时编译为机器码,尽管灵活性较高,但存在以下问题:

启动速度慢:每次启动应用时,字节码需要重新编译。内存占用高:需要维护额外的 JIT 缓存。性能不稳定:即时编译会增加 CPU 负载,影响用户体验。

ART 的改进:引入 AOT 编译ART 使用 AOT 技术,在应用安装时直接编译字节码为机器码并保存。应用运行时不需要即时编译,直接执行机器码,大大提升了性能。

工作原理

AOT 编译发生在应用安装过程中,主要步骤包括:

字节码加载从应用的 .dex 文件(包含应用代码的 Dalvik Executable 格式)加载字节码。静态编译使用编译器(如 dex2oat)将字节码转换为目标设备的机器码。生成 OAT 文件编译后的机器码保存在 OAT 文件中(Optimized Android Runtime 文件),这些文件通常存储在设备的 /data/dalvik-cache 路径下。优化与校验

ART 在编译过程中会根据设备的 CPU 架构(如 ARM 或 x86)生成优化的机器码。还会执行验证(Verification),确保编译后的代码不会造成崩溃或安全问题。

优点

启动更快由于机器码已经编译好,应用启动时无需等待即时编译。性能更高

减少运行时的 JIT 编译负担,提升应用流畅性。更高效的内存使用,因为无需维护 JIT 缓存。

节能省电运行时的 CPU 开销减少,有助于延长电池续航。

缺点

安装时间更长 AOT 编译会显著延长应用安装时间,因为需要完成编译和优化过程。存储空间占用大编译后的 OAT 文件会占用额外的存储空间。灵活性降低动态语言特性(如反射)和运行时修改代码的能力会受到一定限制。

ART 的 AOT 与 JIT 的结合

在 Android 7.0(Nougat)及之后的版本中,ART 引入了 混合编译模式,将 AOT 与 JIT 优势结合:

应用安装时只进行部分 AOT 编译(主要是核心代码)。运行时,使用 JIT 编译未优化的代码片段,同时记录 JIT 编译结果。应用闲置时,通过后台编译(Profile-Guided Optimization, PGO),将热点代码进一步转为 AOT 编译,提高性能。

代码示例与工具

dex2oat 工具 在 Android 系统中,dex2oat 是主要的 AOT 编译工具。你可以使用以下命令查看相关信息:

dex2oat --help

检查 AOT 文件 运行设备上的命令,查看已生成的 OAT 文件:

ls /data/dalvik-cache/

关键函数 art/compiler

bool CompilerDriver::CompileAll() {

// 遍历所有的类和方法

for (Class c : classes) {

for (Method m : c.GetMethods()) {

CompileMethod(m);

}

}

return true;

}

总结

ART 的 AOT 编译通过将字节码提前编译为机器码,显著提升了 Android 应用的启动速度和运行性能。尽管有安装时间较长、存储占用等问题,但通过与 JIT 的结合,ART 在后续版本中进一步优化了运行效率和用户体验,是 Android 性能优化的里程碑技术。

4.2 Just-In-Time 编译 (JIT)

Android Runtime (ART) 的 Just-In-Time (JIT) 编译 是在应用运行时动态编译字节码为机器码的技术,与 Ahead-of-Time (AOT) 编译互为补充,用于提升运行效率并减少应用安装时间。以下是 JIT 编译的详细介绍。

JIT 编译的背景

Dalvik VM 的传统 JIT在 ART 之前,Android 使用的是 Dalvik VM,其核心编译方式就是 JIT。JIT 的特点是按需即时编译,虽然减少了安装时的编译负担,但运行时性能受限,具体表现为:

每次运行时都需要重新编译。占用额外的 CPU 和内存资源,导致运行时性能波动。

ART 的早期缺陷:全量 AOT 编译在早期 ART(Android 5.0 和 6.0)中,全量 AOT 编译优化了运行时性能,但增加了应用安装时间,且存储占用较高。为平衡性能与灵活性,Android 在 7.0(Nougat)引入了 JIT + AOT 混合模式。

JIT 编译的工作原理

ART 中的 JIT 编译在运行时按需触发,以下是其主要工作流程:

字节码解释执行

应用启动时,ART 会使用解释器逐条执行字节码,快速响应用户请求。如果某些代码片段(如循环或常用方法)被多次调用,ART 会将其标记为热点代码。

热点代码的动态编译

当热点代码的执行次数超过一定阈值时,JIT 编译器会将其即时编译为机器码。编译后的机器码保存在内存中,并直接执行,提高运行效率。

Profile 文件记录

在应用运行过程中,ART 会将热点代码的信息记录到 Profile 文件中。这些 Profile 数据可用于后续优化,如后台编译(Profile-Guided Optimization,PGO)。

后台优化与 AOT 的结合

应用空闲时,ART 使用 JIT 记录的 Profile 数据进行增量 AOT 编译,将热点代码进一步优化为高效的机器码。

JIT 的优点

灵活性高

仅对频繁使用的代码进行编译,减少了不必要的编译开销。支持动态语言特性(如反射)和运行时动态加载。

安装时间短

应用安装时无需进行全量 AOT 编译,安装速度更快。

优化运行性能

动态编译热点代码,提升执行效率。配合 Profile 数据进行长期优化。

JIT 的缺点

启动速度相对慢

初次执行代码时,仍需解释执行,直到热点代码触发编译。

内存占用高

编译后的机器码保存在内存中,占用更多 RAM。

性能波动

编译过程需要消耗 CPU 资源,在高负载情况下可能影响用户体验。

JIT + AOT 的混合模式

Android 7.0(Nougat)及之后版本中,ART 实现了 JIT 与 AOT 的混合编译模式,以兼顾运行效率和灵活性:

运行时使用 JIT 编译

应用初次运行时,通过 JIT 编译热点代码,快速优化性能。

后台优化 AOT 编译

应用空闲时,利用 Profile 数据增量编译机器码,生成优化的 OAT 文件,供后续运行使用。

适应设备环境

JIT 和 AOT 编译结果均针对设备的 CPU 架构优化,提供更好的运行效率。

示例与工具

Profile 文件查看Profile 文件通常存储在以下路径:

/data/data//code_cache/profile/

ART 的优化工具使用 profman 工具分析 Profile 数据:

profman --dump-profile-info --profile-file=

实时调试 JIT 编译开启 ART 的 JIT 调试日志:

setprop dalvik.vm.extra-opts -verbose:jit

JIT 编译示例代码 art/runtime/jit

void Jit::CompileMethod(Method* method) {

if (IsHotMethod(method)) {

CompileToMachineCode(method);

}

}

总结

ART 的 JIT 编译通过动态编译热点代码,提供了高效灵活的性能优化方式,与 AOT 编译相辅相成。在应用安装、运行和优化的不同阶段,JIT 编译减少了安装时间,提升了运行性能,同时为后台 AOT 优化提供了数据支持。这种混合编译模式在 Android 系统中实现了性能与灵活性的良好平衡。

4.3 垃圾回收 (GC)

Android Runtime (ART) 的垃圾回收(Garbage Collection, GC)机制负责自动管理应用的内存,释放不再使用的对象所占用的空间,从而避免内存泄漏和提升性能。ART 的垃圾回收相较于 Dalvik 虚拟机有显著改进,以下是详细介绍:

ART 垃圾回收的基本原理

对象分配与生命周期

ART 管理的内存堆分为不同区域(如年轻代和老年代)。应用运行时在堆上分配内存,对象的生命周期决定了它被回收的时机。

垃圾回收触发条件垃圾回收的触发通常基于以下条件:

堆内存不足:新对象分配时发现堆已满。手动触发:某些 API(如 System.gc())可能提示进行回收。内存优化:系统主动回收内存以提升性能。

标记-清除算法ART 的 GC 使用 标记-清除(Mark-and-Sweep) 算法,分为以下步骤:

标记阶段:找到所有活跃的对象(可达的对象)。清除阶段:释放未标记(不可达)的对象所占的内存。

ART 垃圾回收的特点

并发垃圾回收ART 使用并发 GC,将垃圾回收的部分任务分摊到多个线程中,从而减少对主线程的影响,提高应用的流畅性。低延迟设计

ART 针对交互式应用进行了优化,尽可能缩短 GC 的暂停时间(GC Pause Time)。在用户操作频繁的时刻(如滑动列表),ART 尽量避免大规模的回收操作。

分代回收ART 使用分代式垃圾回收策略,将堆分为以下几部分:

年轻代(Young Generation):存放生命周期短的对象,回收频率高。老年代(Old Generation):存放生命周期长的对象,回收频率低。

增量回收ART 的 GC 还支持增量回收(Incremental GC),将一次性的大量回收操作分解为小批量的任务,降低应用响应的卡顿。

ART 的垃圾回收类型

ART 提供多种垃圾回收类型,根据需求自动切换:

Partial GC(部分回收)

仅回收年轻代对象。触发快,暂停时间短。

Full GC(完全回收)

回收整个堆,包括年轻代和老年代。通常在堆内存不足时触发,暂停时间较长。

Concurrent GC(并发回收)

在后台线程执行垃圾回收。减少对主线程的影响,提高性能。

Compacting GC(压缩回收)

重新整理内存堆,将分散的内存块合并为连续的内存。提高后续对象分配的效率,避免内存碎片问题。

Sticky GC(粘性回收)

仅清理应用运行期间新分配的短生命周期对象。触发快,适用于频繁分配对象的场景(如动画或 UI 渲染)。

ART GC 的优化

避免频繁触发 GC

频繁分配短生命周期对象可能导致频繁的垃圾回收,从而影响性能。优化方法:减少临时对象的创建,尤其是在循环中。

减小堆内存压力

控制对象大小,避免一次性分配过多大对象(如 Bitmap)。优化方法:使用 Bitmap.recycle() 主动释放资源。

监控 GC 行为

通过 Android Studio Profiler 或 adb logcat 查看 GC 日志,了解回收频率和暂停时间:

adb logcat | grep GC

调整堆大小

AndroidManifest.xml 中通过 android:largeHeap 属性申请更大的堆内存,但仅在必要时使用。示例:

android:largeHeap="true">

GC 日志示例

以下是 ART GC 的典型日志:

GC concurrent freed 2048K, 10% free 20480K/22528K, paused 5ms+2ms, total 10ms

日志含义:

GC concurrent:并发回收。freed 2048K:释放了 2MB 内存。10% free:堆剩余 10% 的空闲空间。20480K/22528K:堆当前使用 20MB,总大小为 22MB。paused 5ms+2ms:主线程暂停时间分别为 5ms 和 2ms。total 10ms:总回收时间为 10ms。

总结

ART 的垃圾回收机制通过分代式设计、并发回收和增量回收等方式,实现了低延迟、高效的内存管理。这种机制在减少内存泄漏、优化性能方面有显著效果,但开发者仍需遵循最佳实践(如减少对象分配和合理使用内存)来配合 GC 的高效运行,从而保证应用的流畅性和稳定性。

4.4 内存管理与优化

Android Runtime (ART) 的内存模型和优化机制是其高性能运行的核心。相比于早期的 Dalvik 虚拟机,ART 在内存管理和优化方面做出了许多改进,使得应用运行更加高效、流畅且节能。以下是 ART 的内存模型及其优化原理的详细介绍:

ART 的内存模型

ART 的内存模型由以下几个主要部分组成:

1. 堆内存

堆是 ART 中分配对象的主要区域,由以下几个部分组成:

年轻代(Young Generation): 存储生命周期较短的对象。包括 Eden 区和两个 Survivor 区。对象大多在此区域被创建和回收。采用高频率的垃圾回收机制,回收快,暂停时间短。老年代(Old Generation): 存储生命周期较长的对象(多次在年轻代垃圾回收中幸存)。回收频率低,但占用空间大。通常在 Full GC 时进行回收。

2. 栈内存

每个线程都有自己的独立栈,用于存储局部变量、方法调用信息等。栈内存生命周期与线程一致,线程结束时栈内存自动释放。

3. 方法区

用于存储类的元信息(如方法、字段等)。在 ART 中,这部分被称为 Class Metadata,其管理更加高效且占用较少内存。

4. 本地内存(Native Heap)

由 JNI 或底层库分配,与 ART 的堆分开管理。主要用于直接与操作系统交互的资源(如 Bitmap、OpenGL 等)。

ART 内存管理的关键特性

1. 分代式内存管理

ART 实现了分代式内存管理,针对不同生命周期的对象采用不同的策略:

短生命周期对象(年轻代): 采用高效的复制垃圾回收(Copying GC)算法,快速回收。长生命周期对象(老年代): 使用标记-清除(Mark-and-Sweep)算法进行回收,减少停顿时间。粘性垃圾回收(Sticky GC): 仅回收最近分配的短生命周期对象,适用于频繁内存分配的场景。

2. 内存对齐与压缩

ART 优化了内存分配的方式:

对象内存对齐: 保证对象存储在对齐的地址上,提升访问效率。指针压缩(Pointer Compression): 在 64 位架构下,通过压缩指针(将 64 位指针压缩为 32 位),减少内存占用。

3. 增量分配与回收

ART 引入增量内存分配和回收机制,将内存操作分散到多个时刻,降低单次分配或回收的延迟。

4. 垃圾回收优化

并发垃圾回收(Concurrent GC): 在后台线程完成标记和清除工作,减少主线程的阻塞时间。压缩垃圾回收(Compacting GC): 防止内存碎片,通过整理内存堆空间提升后续分配效率。

ART 内存优化的核心技术

1. 即时与延迟分配

ART 在对象分配时,使用线程本地分配缓冲区(Thread Local Allocation Buffer, TLAB):

分配效率提升: 每个线程独立分配小块内存,减少锁竞争。减少全局锁冲突: 提高多线程环境下的内存分配效率。

2. 内存释放与复用

ART 优化了内存释放的机制:

回收后的内存块会直接进入内存池,供后续对象分配复用,减少操作系统层的内存申请。

3. 代码与数据共享

Dex 文件优化: ART 将 .dex 文件编译为更高效的 OAT 文件,减少内存消耗。字符串池: ART 实现了全局字符串池,共享重复字符串以减少内存占用。

4. 多用户优化

ART 针对 Android 多用户模式优化了资源共享机制,例如系统类的元信息仅加载一次并共享。

ART 内存模型优化的效果

1. 提高运行性能

减少垃圾回收引起的停顿时间,提升应用响应速度。增强内存分配效率,特别是高频次对象分配场景(如 UI 渲染)。

2. 降低内存占用

通过指针压缩、字符串池等技术,减少应用的内存足迹。内存复用机制避免了频繁的内存分配和回收。

3. 提升设备续航

更高效的内存管理降低了 CPU 和内存的使用率,从而减少电池消耗。

开发者优化内存的最佳实践

1. 避免内存泄漏

Context 管理: 避免长生命周期对象持有短生命周期的 Context。工具检测: 使用 Android Studio Profiler 或 LeakCanary 检测内存泄漏。

2. 减少临时对象分配

避免在循环中频繁分配短生命周期对象。使用对象池(Object Pool)复用高频创建的对象。

3. 优化大对象使用

对于 Bitmap 等大对象,使用 Bitmap.recycle() 主动释放内存。尽量使用合适的压缩格式(如 WebP)降低图片大小。

4. 使用 Android 平台特性

配置适当的 android:largeHeap 仅在必要时使用。利用 Profile-Guided Optimization (PGO) 提升运行时效率。

总结

ART 的内存模型通过分代管理、并发回收、压缩优化等技术,提供了高效的内存分配和回收机制,减少了内存泄漏和性能瓶颈。此外,ART 针对特定设备和应用场景的优化(如指针压缩和 TLAB)显著提升了应用的运行效率。开发者需结合 ART 的内存特性,配合最佳实践,确保应用在各种设备上的内存使用和性能表现达到最佳状态。

5. 源码剖析

5.1 ART 初始化

ART 在启动时初始化环境,包括类加载器、GC 和编译器等。

源码路径:

art/runtime/runtime.cc 关键函数:

void Runtime::Init() {

// 初始化 GC

InitGC();

// 初始化编译器

InitCompiler();

// 加载基本类

LoadBaseClasses();

}

5.2 方法执行

ART 通过解释器或编译器执行方法。

源码路径:

art/runtime/interpreter/interpreter.cc 核心代码:

void Interpreter::ExecuteMethod(Thread* thread, Method* method) {

if (method->IsCompiled()) {

ExecuteCompiledCode(thread, method);

} else {

ExecuteDexCode(thread, method);

}

}

6. 性能优化的设计理念

冷热分离:通过 JIT 编译和 Profiling 分析,将热方法进行动态优化。减少停顿时间:通过并发 GC 减少应用的响应延迟。内存优化:利用 TLAB 和大型对象空间优化内存分配。

7. 总结

ART 作为 Android 平台的核心运行时环境,大幅度提升了应用性能和用户体验。它的 AOT 和 JIT 编译器、并发垃圾回收和先进的内存管理为 Android 应用的高效运行提供了强有力的保障。

通过分析 ART 源码,我们能够深入理解其设计理念和实现细节,为开发高性能 Android 应用打下坚实基础。如果您有更多深入学习的需求,可以进一步探索 ART 的调试工具和优化方法。

参考

Memory Management in Android Android CPU, Compilers, D8 & R8

相关推荐