使用 CPU Profiler 检查 CPU Activity 和函数跟踪

CPU Profiler 可帮助您实时检查应用的 CPU 使用率和线程 Activity,并记录函数跟踪,以便您可以优化和调试您的应用代码。

要打开 CPU Profiler,请按以下步骤操作:

  • 点击 View > Tool Windows > Android Profiler(也可以点击工具栏中的 Android ProfilerInspect CPU activity - 图1)。
  • 从 Android Profiler 工具栏中选择您想要分析的设备和应用进程。如果您通过 USB 连接了某个设备但该设备未在设备列表中列出,请确保您已启用 USB 调试
  • 点击 CPU 时间线中的任意位置即可打开 CPU Profiler。

为什么要分析 CPU 使用率

最大限度减少应用的 CPU 使用率具有许多优势,如提供更快更顺畅的用户体验,以及延长设备电池续航时间。 它还可帮助应用在各种新旧设备上保持良好性能。 与应用交互时,您可以使用 CPU Profiler 监控 CPU 使用率和线程 Activity。 不过,如需了解应用如何执行其代码的详细信息,您应记录和检查函数跟踪

对于应用进程中的每个线程,您可以查看一段时间内执行了哪些函数,以及在其执行期间每个函数消耗的 CPU 资源。 您还可以使用函数跟踪来识别调用方被调用方。调用方指调用其他函数的函数,而被调用方是指被其他函数调用的函数。 您可以使用此信息确定哪些函数负责调用常常会消耗大量特定资源的任务,并尝试优化应用代码以避免不必要的工作。

如果您想收集可帮助您检查原生系统进程的详细系统级数据,并解决掉帧引起的界面卡顿,您应使用 systrace

或者,如果您想导出您使用 Debug 类捕获的 .trace 文件,您应使用 Traceview

CPU Profiler 概览

当您打开 CPU Profiler 时,它将立即开始显示应用的 CPU 使用率和线程 Activity。 您应该会看到类似图 1 的一些内容:

Inspect CPU activity - 图2

图 1. CPU Profiler。

如图 1 所示,CPU Profiler 的默认视图包括以下内容:

  • Event 时间线: 显示应用中在其生命周期不同状态间转换的 Activity,并表明用户与设备的交互,包括屏幕旋转 Event。 如需了解有关 Event 时间线的更多信息,包括如何启用它,请阅读 启用高级分析
  • CPU 时间线: 显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用使用的总线程数。 此时间线还显示其他进程的 CPU 使用率(如系统进程或其他应用),以便您可以将其与您的应用使用率进行对比。 通过沿时间线的水平轴移动鼠标,您还可以检查历史 CPU 使用率数据。
  • 线程 Activity 时间线: 列出属于应用进程的每个线程并使用下面列出的颜色沿时间线标示它们的 Activity。 在您记录一个函数跟踪后,您可以从此时间线中选择一个线程以在跟踪窗格中检查其数据。
    • 绿色: 表示线程处于活动状态或准备使用 CPU。 即,它正在“运行中”或处于“可运行”状态。
    • 黄色: 表示线程处于活动状态,但它正在等待一个 I/O 操作(如磁盘或网络 I/O),然后才能完成它的工作。
    • 灰色: 表示线程正在休眠且没有消耗任何 CPU 时间。 当线程需要访问尚不可用的资源时偶尔会发生这种情况。 线程进入自主休眠或内核将此线程置于休眠状态,直到所需的资源可用。
  • 记录配置: 允许您选择以下选项之一以确定分析器记录函数跟踪的方式。
    • Sampled: 一个默认配置,在应用执行期间频繁捕获应用的调用堆栈。 分析器比较捕获的数据集以推导与应用代码执行有关的时间和资源使用信息。 基于“Sampled”的跟踪的固有问题是,如果应用在捕获调用堆栈后进入一个函数并在下一次捕获前退出该函数,则分析器不会记录该函数调用。 如果您对此类生命周期很短的跟踪函数感兴趣,您应使用“Instrumented”跟踪。
    • Instrumented: 一个默认配置,在运行时设置应用以在每个函数调用的开始和结束时记录时间戳。 它收集时间戳并进行比较,以生成函数跟踪数据,包括时间信息和 CPU 使用率。 请注意,与设置每个函数关联的开销会影响运行时性能,并可能会影响分析数据,对于生命周期相对较短的函数,这一点更为明显。 此外,如果应用短时间内执行大量函数,则分析器可能会迅速超出它的文件大小限制,且不能再记录更多跟踪数据。
    • Edit configurations: 允许您更改上述“Sampled”和“Instrumented”记录配置的某些默认值,并将它们另存为自定义配置。 如需了解更多信息,请转到创建记录配置部分。
  • 记录按钮: 用于开始和停止记录函数跟踪。 如需了解更多信息,请转到记录和检查函数跟踪部分。
    注: 分析器还会报告 Android Studio 和 Android 平台添加到您的应用进程(如 JDWPProfile SaverStudio:VMStatsStudio:Perfa 以及 Studio:Heartbeat,尽管它们在线程 Activity 时间线中显示的确切名称可能有所不同)的线程 CPU 使用率。 这表示 CPU 时间线中应用的 CPU 使用率还可反映这些线程使用的 CPU 时间。 您可以在线程 Activity 时间线中查看其中的一些线程并监控其 Activity。 (不过,由于分析器线程执行原生代码,因此,您无法为它们记录函数跟踪数据。)Android Studio 将报告此数据,以便当线程 Activity 及 CPU 使用率实际上是由应用代码引发时,您可以轻松识别。

记录和检查函数跟踪

要开始记录函数跟踪,从下拉菜单中选择 SampledInstrumented 记录配置,或选择您创建的自定义记录配置,然后点击 RecordInspect CPU activity - 图3。 与应用交互并在完成后点击 Stop recordingInspect CPU activity - 图4。 分析器将自动选择记录的时间范围,并在函数跟踪窗格中显示其跟踪信息,如图 2 所示。如果您想检查另一个线程的函数跟踪,只需从线程 Activity 时间线中选中它。

Inspect CPU activity - 图5

图 2. 记录函数跟踪后的 CPU Profiler。

  • 选择时间范围: 用于确定您要在跟踪窗格中检查所记录时间范围的哪一部分。 当您首次记录函数跟踪时,CPU Profiler 将在 CPU 时间线中自动选择您的记录的完整长度。 如果您想仅检查所记录时间范围一小部分的函数跟踪数据,您可以点击并拖动突出显示的区域边缘以修改其长度。
  • 时间戳: 用于表示所记录函数跟踪的开始和结束时间(相对于分析器从设备开始收集 CPU 使用率信息的时间)。 在选择时间范围时,您可以点击时间戳以自动选择完整记录,如果您有多个要进行切换的记录,则此做法尤其有用。
  • 跟踪窗格: 用于显示您所选的时间范围和线程的函数跟踪数据。 仅在您至少记录一个函数跟踪后此窗格才会显示。 在此窗格中,您可以选择想如何查看每个堆叠追踪(使用跟踪标签),以及如何测量执行时间(使用时间引用下拉菜单)。
  • 选择后,可通过 Top Down 树、Bottom Up 树、调用图表火焰图的形式显示您的函数跟踪。 您可以在下文中了解每个跟踪窗格标签的更多信息。
  • 从下拉菜单中选择以下选项之一,以确定如何测量每个函数调用的时间信息:
    • Wall clock time:壁钟时间信息表示实际经过的时间。
    • Thread time:线程时间信息表示实际经过的时间减去线程没有消耗 CPU 资源的任意时间部分。 对于任何给定函数,其线程时间始终少于或等于其壁钟时间。 使用线程时间可以让您更好地了解线程的实际 CPU 使用率中有多少是给定函数消耗的。

使用 Call Chart 标签检查跟踪

Call Chart 标签提供函数跟踪的图形表示形式,其中,水平轴表示函数调用(或调用方)的时间段和时间,并沿垂直轴显示其被调用者。 对系统 API 的函数调用显示为橙色,对应用自有函数的调用显示为绿色,对第三方 API(包括 Java 语言 API)的函数调用显示为蓝色。 下面的图 3 展示了一个调用图表示例,并描绘了给定函数的 self time、children time 以及总时间的概念。 您可以在如何使用 Top Down 和 Bottom Up 检查跟踪部分详细了解这些概念。

Inspect CPU activity - 图6

图 3. 一个调用图表示例,描绘了函数 D 的 self、children 及总时间。

提示: 若要跳转到某个函数的源代码,请右键点击该函数并选择 Jump to Source。 这适用于任一跟踪窗格标签。

使用 Flame Chart 标签检查跟踪

Flame Chart 标签提供一个倒置的调用图表,其汇总相同的调用堆栈。 即,收集共享相同调用方顺序的完全相同的函数,并在火焰图中用一个较长的横条表示它们(而不是将它们显示为多个较短的横条,如调用图表中所示)。 这样更方便您查看哪些函数消耗最多时间。 不过,这也意味着水平轴不再代表时间线,相反,它表示每个函数相对的执行时间。

为帮助说明此概念,请考虑以下图 4 中的调用图表。 请注意,函数 D 多次调用 B(B1、B2 和 B3),其中一些对 B 的调用也调用了 C(C1 和 C3)。

Inspect CPU activity - 图7

图 4. 包含多个共享通用调用方顺序的函数调用的调用图表。

由于 B1、B2 和 B3 共享相同的调用方顺序 (A → D → B),因此,可将它们汇总在一起,如下所示。 同样,将 C1 和 C3 汇总在一起,因为它们也共享相同的调用方顺序 (A → D → B → C)—请注意,未包含 C2,因为它具有不同的调用方顺序 (A → D → C)。

Inspect CPU activity - 图8

图 5. 汇总共享相同调用堆栈的相同函数。

汇总的函数调用用于创建火焰图,如图 6 所示。请注意,对于火焰图中任何给定的函数调用,消耗最多 CPU 时间的被调用方首先显示。

Inspect CPU activity - 图9

图 6. 图 4 中显示的调用图表的火焰图表示形式。

使用 Top Down 和 Bottom Up 检查跟踪

Top Down 标签显示一个函数调用列表,在该列表中展开函数节点会显示函数的被调用方。 图 7 显示图 3 中调用图表的“Top Down”图表。图表中的每个箭头都从调用方指向被调用方。

如图 7 所示,在“Top Down”标签中展开函数 A 的节点可显示它的被调用方,即函数 B 和 D。 然后,展开函数 D 的节点可显示它的被调用方,即函数 B 和 C 等等。 与 Flame chart 标签相似,“Top Down”树汇总共享相同调用堆栈的相同函数的跟踪信息。 也就是说,Flame chart 标签可提供Top down 标签的图形化表示形式。

Top Down 标签提供以下信息以帮助说明在每个函数调用上所花费的 CPU 时间(时间也可以用线程总时间占所选时间范围的持续时间的百分比表示):

  • Self: 表示函数调用在执行自己的代码(而非被调用方的代码)上所花的时间,如图 3 中的函数 D 所示。
  • Children: 表示函数调用在执行自己的被调用方(而非自己的代码)上所花的时间,如图 3 中的函数 D 所示。
  • 总和: 函数的 SelfChildren 时间的总和。 这表示应用在执行函数调用上所花的总时间,如图 3 中函数 D 所示。

Inspect CPU activity - 图10
图 7. 一个“Top Down”树。

Inspect CPU activity - 图11
图 8. 图 7 中函数 C 的“Bottom Up”树。

Bottom Up 标签显示一个函数调用列表,在该列表中展开函数节点将显示函数的调用方。 通过使用图 7 中展示的跟踪示例,图 8 为函数 C 提供了一个“Bottom Up”树。 在“Bottom Up”树中打开函数 C 的节点可显示它独有的调用方,即函数 B 和 D。请注意,尽管 B 调用 C 两次,但在“Bottom Up”树中展开函数 C 的节点时,B 仅显示一次。 然后,展开 B 的节点显示其调用方,即函数 A 和 D。

Bottom Up 标签用于按照消耗最多(最少)CPU 时间排序函数。 您可以检查每个节点以确定在调用函数上哪些调用方花了最多 CPU 时间。 与“Top Down”树相比,“Bottom Up”树中的每个函数的时间信息引用每个树顶部的函数(顶部模式)。 CPU 时间也可表示为在该记录期间占线程总时间的百分比。 下表有助于阐述如何解释顶部节点的时间信息及其调用方函数(子节点)。

SelfChildren合计
“Bottom Up”树顶部的函数(顶部模式)表示函数在执行自己的代码(而非其被调用方的代码)上所花的时间。 与“Top Down”树相比,此时间信息表示在记录的持续时间内对此函数所有调用的总和。表示函数执行它的被调用方(而非它自己的代码)上所花的总时间。 与“Top Down”树相比,此时间信息表示在记录的持续期间内所有对此函数被调用方的调用总和。self time 和 children time 的总和。
调用方函数(子节点)表示在由调用方调用时被调用方的总 self time。 以图 8 中的“Bottom Up”树为例,被 B 调用时,函数 B 的 self time 将等于函数 C 每个执行的 self time 的总和。表示在由调用方调用时被调用方的总 children time。 以图 8 中的“Bottom Up”树为例,被 B 调用时,函数 B 的 children time 将等于函数 C 每个执行的 children time 的总和。self time 和 children time 的总和。

注:对于给定录制,当分析器到达文件大小限制时,Android Studio 将停止收集新数据(不过,这不会停止记录)。 在执行“Instrumented”跟踪时,这种情况通常会更快发生,因为与“Sampled”跟踪相比,此类跟踪在较短时间里会收集更多数据。 如果您将检查时间范围延长至达到限制后发生的记录时间,则跟踪窗格中的时间数据不会发生变化(因为没有新数据可用)。 此外,当您仅选择没有数据可用的记录部分时,对于时间信息,跟踪窗格将显示 NaN

创建记录配置

您可以从 Android Studio 为您提供的记录配置中选择一个配置,如 SampledInstrumented,也可以创建自己的记录配置。要创建或编辑自定义配置,或检查现有默认配置,可通过从记录配置下拉菜单中选择 Edit configurations 来打开 CPU Recording Configurations 对话框。

Inspect CPU activity - 图12

图 9. 您可以通过 CPU Recording Configurations 对话框创建或编辑自定义记录配置,或检查现有默认配置。

您可以通过从左侧窗格中选择现有配置来检查其设置,也可按如下方式创建一个新的记录配置:

  • 点击对话框左上角的 AddInspect CPU activity - 图13。 这将创建一个包含一些默认设置的新配置。
  • 为您的配置命名。
  • Trace Technology 部分选择 SampledInstrumented。其中每一个选项都按 CPU Profiler 概览 中所述运行。
  • 对于“Sampled”记录配置,以微秒 (μs) 为单位指定 Sampling interval。此值表示应用调用堆栈的每个抽样之间的持续时间。请记住,您指定的持续时间越短,您就会越快到达所记录数据的文件大小限制。
  • 对于写入连接设备的记录数据,以兆字节 (MB) 为单位指定 File size limit。当您停止记录时,Android Studio 将解析此数据并将其显示在分析器窗口中。因此,如果您增加此限制并记录大量数据,Android Studio 将花更长时间解析文件,并可能会无响应。
    注: 如果您使用运行 API 级别 26 或更高版本的连接设备,则对于跟踪数据的文件大小没有限制,此值可忽略。不过,您仍需留意每次记录后设备收集了多少数据——Android Studio 可能难以解析大型跟踪文件。例如,当应用短时间内调用许多函数时,如果您记录抽样间隔较短的“Sampled”跟踪或“Instrumented”跟踪,您很快就会生成大型跟踪文件。

  • 点击 ApplyOK。 如果您更改了其他记录配置,则也将应用这些更改。
    创建新配置后,系统将自动从记录配置下拉菜单中选中它,并且您可以将它用于您的下一次记录。