Android 的垃圾回收工作负载





0/5 (0投票)
Android 的垃圾回收工作负载
Intel® Developer Zone 提供跨平台应用开发工具和操作指南、平台和技术信息、代码示例以及同行专业知识,帮助开发者创新并取得成功。加入我们的社区,了解 Android、物联网、Intel® RealSense™ 技术 和 Windows,下载工具、获取开发套件、与志同道合的开发者交流想法,并参与黑客松、竞赛、路演和本地活动。
衡量 Android 垃圾回收性能的新方法
随着移动计算市场的不断发展,Google Android* 已成为智能移动设备最受欢迎的软件栈之一。由于 Java* 是 Android 应用的主要实现语言,Java 虚拟机 (JVM) 对提供最佳 Android 用户体验至关重要。
JVM 的垃圾回收器 (GC) 组件是最重要的组成部分之一。它提供自动内存管理,并确保 Java 程序员不会因错误地释放内存而意外(或故意)导致 JVM 崩溃,从而提高程序员的生产力和系统安全性。GC 会跟踪 Java 程序当前引用的对象,以便回收未引用对象占用的内存,这些对象也称为“垃圾”。准确高效的垃圾回收对于实现 Android 应用的高性能和用户体验至关重要。
GC 通过引入开销,可能会干扰用户线程的执行,从而影响性能和用户体验。一个典型的例子是,大多数 GC 算法都需要在某个时刻暂停应用程序的所有 Java 线程,以确保只回收垃圾对象占用的内存。如果暂停时间过长,可能会导致性能和用户体验问题,例如卡顿(UI 无响应或暂时迟滞)以及缺乏响应性和流畅性。此外,GC 通常由 JVM 在后台自动触发,程序员对 GC 调度几乎没有控制权。Android 开发者必须意识到这个隐藏的软件组件。
为了通过优化 GC 来实现最佳性能和用户体验,反映 GC 性能的工作负载是必不可少的。然而,根据我们的经验,大多数流行的 Android 工作负载(游戏、解析、安全等)仅间歇性地对 GC 产生压力。Intel 开发了用于 Android 的垃圾回收工作负载 (GCW for Android),用于分析 GC 性能及其对 Android 性能和用户体验的影响。
GCW for Android 重点测试 Android Runtime (ART) 的内存管理子系统。它旨在代表 Android 应用的峰值内存使用行为,因此基于 GCW for Android 分析进行的优化不仅可以提高工作负载得分,还可以改善用户体验。此外,GCW for Android 提供选项供您调整其行为,使其足够灵活,可以模拟不同类型的应用程序行为。
GCW for Android 概述
Intel 开发的 GCW for Android 基于对真实世界应用(包括商业、通信和娱乐等类别中的典型 Android 应用)的分析。GCW for Android 是一种对象分配和 GC 密集型工作负载,专为 GC 性能评估和分析而设计。
该工作负载有两种工作模式。您可以从命令行运行它,或使用 GUI 进行控制。GCW for Android 是可配置的。您可以指定工作负载大小、分配线程数、对象分配大小分布和对象生命周期分布,以适应不同的情况。它提供了测试不同分配行为的 GC 的灵活性,因此您可以使用 GCW for Android 来分析大多数使用情况下的 GC 性能。
GCW for Android 包含多个指标。它报告总执行时间作为 GC 和对象分配效率的主要指标。该工作负载还基于 Android 日志记录系统报告内存使用信息,例如 Java 堆占用空间和已分配对象总字节数。
如何运行 GCW for Android
对于 Android 平台,GCW for Android 以单个软件包提供:GCW_Android.apk。安装 apk 后,点击 GCW_Android 图标将启动工作负载并显示包含“开始”和“设置”按钮的 UI。
点击“开始”按钮将使用默认配置文件设置运行工作负载。“默认”设置选项用于将工作负载配置文件重置为默认值。如果您想更改配置,请点击“设置”按钮。
第一个设置是可选择的配置文件列表。目前,“默认”是唯一的选项。配置文件包含工作负载使用的参数,默认配置文件源自多个真实应用的特征。
总对象大小:允许您定义所有分配线程在一个迭代中分配的总对象大小。默认值为 100MB,这意味着当以四线程的多线程模式运行时,每个线程在压力测试阶段的单次迭代中将分配价值 25MB 的对象。
桶大小:允许您定义在单个分配阶段构建的二叉树的大小。默认值为 1MB。
大对象大小:允许您定义大对象的大小。默认值为 12KB,这是 ART 在大对象空间中分配的最小尺寸对象。
对象大小分布:允许您定义分配对象的尺寸分布。总和应为 100%。
大对象元素类型分布:允许您定义每个对象的生命周期。对象的生命周期以对象大小(默认为 1MB)为单位衡量,从其创建到其变得不可访问。生命周期数据中的第一个项是长生命周期对象百分比(运行整个工作负载的对象百分比),第二个是第一个周期后死亡的对象百分比,第三个是第二个周期后死亡的对象百分比(在分配了价值 1MB 的其他对象后),而 K 项是 K+1 周期后死亡的对象百分比(在分配了价值 K MB 的对象后)。项目之间用逗号分隔,每行应包含相同数量的项目。
默认情况下,工作负载以多线程模式运行。如果要以单线程模式运行,请勾选“单线程运行?”
为了理解 GCW for Android 如何反映 JVM 的内存管理特性并代表真实应用,让我们更深入地了解 GCW for Android 的设计。
GCW for Android 设计
GCW for Android 的设计旨在模拟真实应用表现出的 JVM 内存管理行为。对大量流行 Android 应用中的不同用户场景进行详细分析表明,Java 程序会创建具有不同生命周期的各种大小的对象,工作负载也是如此。此外,对象大小和生命周期之间的默认关系(小对象倾向于具有短生命周期)以及多线程分配行为与真实应用相似。
从分析数据中抽象出以下特性,作为 GCW for Android 的主要设计要点。
对象大小分布
图 3 显示了基于 17 款流行 Google Play* 应用的对象大小分布直方图。X 轴是给定对象大小范围的全部对象百分比,Y 轴是对象大小范围的存储桶。
运行时,大约 80% 的已分配对象是小于或等于 64 字节的小对象。我们在大约 50 款更流行的应用上也观察到了相同的行为,因此 GCW for Android 使用了各种大小的对象。下一节“GCW for Android 工作流程”将讨论 GCW for Android 如何模拟对象大小。
对象生命周期
对象生命周期是通过对象在 Java 堆中存活多少次垃圾回收来衡量的。了解对象生命周期可以帮助 JVM 开发人员通过优化 GC 的工作方式和调整与 GC 相关的选项来优化 GC 性能。
在我们进行的调查中,绘制对象大小与对象生命周期关系的曲线显示两者之间存在松散的关系,但生命周期似乎与对象大小有关。这里我们以 Gallery3D(图 4)和 Google Maps*(图 5)为例。X 轴是死亡的对象百分比;Y 轴是存活的 GC 次数。每条线代表一个对象大小范围。
大多数对象在一次到三次 GC 后死亡,但曲线并不完全一致。对于 Google Maps,约 80% 的 1-16 字节对象在第一次 GC 后死亡,但只有 60% 的 33-64 字节对象在第一次 GC 后死亡。不同大小的对象可以具有不同的生命周期,因此 GCW for Android 对对象生命周期的反映程度是一个重要的设计目标。下一节“GCW for Android 工作流程”将讨论 GCW for Android 如何模拟对象生命周期。
多线程
我们的调查表明,大多数 Android Java 应用都是多线程的,尽管线程之间通常没有多少通信。在 Android 设备上运行的每个 Java 应用可能都有多个线程同时在 Java 堆中分配对象。GCW for Android 支持多线程分配,以模拟这种真实的应用特性。下一节“GCW for Android 工作流程”将解释 GCW for Android 如何支持多线程。
总而言之,GCW for Android 中模拟了以下工作负载特性。典型 Android 应用
- 分配各种大小的对象。
- 具有相似的已分配对象大小分布。
- 已分配对象具有不同的生命周期,并且它们的生命周期似乎与其大小有关。
- 在多个线程中并行分配对象。
将所有这些观察结果结合起来,我们设计了 GCW for Android 工作流程,使其不仅是一个 JVM 内存管理工作负载,而且能够反映实际的使用场景。
GCW for Android 工作流程
GCW for Android 支持两种线程模式:单线程和多线程。在多线程模式下,每个线程都被分配一个编号,该编号默认为逻辑 CPU 编号。您可以更改它。
在内部,GCW for Android 构建了多个二叉树来管理对象的大小和生命周期。它通过在树中插入和删除节点来构建和删除对象,如图 6(b) 所示。
图 6(a) 和 (b) 显示了 GCW for Android 的工作方式。它首先启动一定数量的线程,然后每个线程遵循相同的逻辑:分配长生命周期对象,然后进行压力测试。压力测试是工作负载中最耗时的部分,是一个大循环,迭代次数是可配置的,默认值为 100。在每次迭代中,GCW for Android 会分配一个可配置的字节数(默认为 100MB),其中默认包含 6 种小对象(16、24、40、96、192 和 336 字节的对象)以及大对象(12K 字节,可配置)。小对象被创建为二叉树的节点;大对象被创建为字节、字符、整数或长整型数组。在 GCW for Android 的每次迭代中,工作负载
- 构建二叉树。
- 根据小对象的生命周期从已构建的树中删除一些节点。
- 构建大对象数组。
- 根据大对象的生命周期删除一些数组。
现在让我们仔细看看 GCW for Android 的内部设计,以了解它是如何实现上述内存系统使用仿真的特性的。
对象大小分布
为了模拟常见的对象大小分布模式,GCW for Android 内部定义了七个对象大小存储桶
- 16 字节 → [1-16B]
- 24 字节 → [17-32B]
- 40 字节 → [33-64B]
- 96 字节 → [65-128B]
- 192 字节 → [129-256B]
- 336 字节 → [257-512B]
- 大对象(默认 12KB)
为了方便地抽象对象引用,GCW for Android 将小尺寸对象分配为二叉树的节点,大对象被创建为数组。大对象数组有四种类型:字节、字符、整数和长整型。
现在我们来看看 GCW for Android 如何管理对象生命周期以模拟真实应用。
对象生命周期控制
GCW for Android 在内部使用二叉树来控制对象生命周期。每个二叉树都有一个预定义的生命周期。GCW for Android 控制树的生命周期,从而控制对象生命周期。
例如,假设您希望您的对象生命周期模型有三个阶段,其中 50% 的对象在 K 周期死亡,25% 的对象在之后 K+1 周期死亡,其余 25% 在 K+2 周期存活。
在 K 周期开始时,GCW for Android 会同时构建三个树,每个树对应一个生命周期阶段。在 K 周期,持有 50% 对象的一个树通过将 null 赋值给根对象而变得不可达,从 GC 的角度来看,这使得整个树变得不可达。因此,50% 的对象在 K 周期后被 GC 回收。然后在 K+1 周期,持有 25% 对象(通过将根设置为 null)的第二个树变得不可达,从而导致 25% 的对象在 K+1 周期后被回收,并留下 25% 的对象在 K+2 周期(参见图 7)存活。
在不同的使用场景下,对象生命周期不是确定的,因此 GCW for Android 也不使对象生命周期确定。为了模拟真实应用,GCW for Android 为每个线程生成一个随机数,每个线程都有不同的种子,以决定节点的对象大小和生命周期阶段。例如。
在此示例中,三个 24 字节的对象将在第一阶段后死亡,其他对象将在第 0 阶段后死亡。
总而言之,GCW for Android 可以轻松地反映真实应用的对象的分配和 GC 行为。使用 GCW for Android 可以帮助您识别 ART 中的许多机会,以增强性能和用户体验。
使用 GCW for Android 发现的机会
在我们进行性能调查期间,我们发现对象分配通常是应用程序中最关键的部分。进一步调查表明,通过内联 RosAlloc 分配快速路径可以加快分配速度,这意味着消除了对分配函数的调用。这带来了 GCW for Android 约 7% 的改进。该实现已合并到 Android 开源项目 (AOSP)。
此外,我们发现通过消除免疫空间之间不必要的卡片表处理,可以减少 GC 标记时间。卡片表是一种记录跨空间对象引用的机制,它保证了 GC 在仅收集 Java 堆的子集(称为“空间”)而不是整个堆时的正确性。分析还显示,通过在部分和完整 GC 期间清除分配空间的脏卡片,可以减少 GC 暂停时间。最小化暂停时间对于最终用户应用至关重要,因为减少暂停时间可以减少卡顿并提高流畅性。
GCW for Android 还帮助我们找到了 GC 优化机会,例如并行标记和并行扫描。Intel 已将其中一些优化贡献给了 AOSP,其余的已添加到 Intel ART 二进制文件中。总而言之,这些优化使 GCW for Android 性能提升了约 20%。所有这些都帮助改善了 Intel 产品的性能和用户体验。
开源 GCW for Android
我们已提交 GCW for Android 到 AOSP 进行上游化,使其可供整个 Android 开发社区使用。我们的目标是使 GCW for Android 成为最真实的 Android Java 内存系统工作负载,并利用它来驱动所有平台的 GC 优化,以提高 Android 的性能和用户体验。
下载次数
代码可以从 Google 下载,地址为
https://android-review.googlesource.com/#/c/167279/
结论
GCW for Android 是一种 JVM 内存管理工作负载,旨在模拟真实应用如何使用 Java 内存管理。它旨在帮助 JVM 和应用程序开发人员优化 Android 上的内存管理和用户体验。Intel 正在利用它来识别 ART 中的优化机会,以提高产品性能和用户体验。我们希望它能成为 Android 性能和用户体验的重要指标。
参考文献
Java 虚拟机 (JVM): https://en.wikipedia.org/wiki/Java_virtual_machine
垃圾回收 (GC): https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
Android Runtime (ART): https://aosp.org.cn/devices/tech/dalvik/
深入了解 Java 虚拟机: https://www.artima.com/insidejvm/ed2/index.html
致谢(按字母顺序)
Jean Christophe Beyler、Dong Yuan Chen、Haitao Feng、Jean-Philippe Halimi、Paul Hohensee、Aleksey Ignatenko、Rahul Kandu、Lei Li、Desikan Saravanan、Kumar Shiv 和 Sushma Kyasaralli Thimmappa。
关于作者
Li Wang 是 Intel 软件与解决方案事业部 (SSG)、系统技术与优化部 (STO)、客户端软件优化部 (CSO) 的一名软件工程师。她专注于 Android 工作负载开发和 Android Java 运行时中的内存管理优化。
Lin Zang 是 Intel 软件与解决方案事业部 (SSG)、系统技术与优化部 (STO)、客户端软件优化部 (CSO) 的一名软件工程师。他专注于 Android Java 运行时中的内存管理优化和功能稳定性。