It's happening now, watch the livestream.

使用 CPU Profiler 检查 CPU 活动

优化应用的 CPU 使用率具有许多优势,如提供更快且更顺畅的用户体验,以及延长设备电池续航时间。

您可以使用 CPU Profiler 在与应用交互时实时检查应用的 CPU 使用率和线程活动,也可以检查记录的方法跟踪数据、函数跟踪数据和系统跟踪数据的详细信息。

CPU Profiler 记录和显示的特定信息类型由您选择的记录配置确定:

  • 系统跟踪数据:捕获精细的详细信息,以便您检查应用与系统资源的交互情况。
  • 方法和函数跟踪数据:对于应用进程中的每个线程,您可以了解一段时间内执行了哪些方法 (Java) 或函数 (C/C++),以及每个方法或函数在其执行期间消耗的 CPU 资源。您还可以使用方法和函数跟踪数据来识别调用方和被调用方。调用方是指调用其他方法或函数的方法或函数,而被调用方是指被其他方法或函数调用的方法或函数。您可以使用此信息来确定哪些方法或函数负责调用常常会消耗大量资源的特定任务,并优化应用的代码以避免不必要的工作。

    记录方法跟踪数据时,您可以选择“sampled”或“instrumented”记录。记录函数跟踪数据时,只能使用“sampled”记录。

如需详细了解如何使用和选择其中每个跟踪选项,请参阅选择记录配置

CPU Profiler 概览

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

  1. 依次选择 View > Tool Windows > Profiler,或点击工具栏中的 Profile 图标

    如果 Select Deployment Target 对话框提示,请选择要将您的应用部署到哪个设备上以进行分析。如果您已通过 USB 连接设备但系统未列出该设备,请确保您已启用 USB 调试

  2. 点击 CPU 时间轴上的任意位置以打开 CPU Profiler。

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

图 1. CPU Profiler 中的时间轴。

如图 1 所示,CPU Profiler 的默认视图包括以下时间轴:

  1. 事件时间轴:显示应用中的 Activity 在其生命周期内不断转换而经历各种不同状态的过程,并指示用户与设备的交互,包括屏幕旋转事件。如需了解如何在搭载 Android 7.1(API 级别 25)及更低版本的设备上启用事件时间轴,请参阅启用高级分析
  2. CPU 时间轴:显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用当前使用的线程总数。此时间轴还显示其他进程(如系统进程或其他应用)的 CPU 使用率,以便您可以将其与您应用的使用率进行对比。您可以通过沿时间轴的水平轴移动鼠标来检查历史 CPU 使用率数据。
  3. 线程活动时间轴:列出属于应用进程的每个线程,并使用下面列出的颜色在时间轴上指示它们的活动。记录跟踪数据后,您可以从此时间轴上选择一个线程,以在跟踪数据窗格中检查其数据。
    • 绿色:表示线程处于活动状态或准备使用 CPU。也就是说,它处于正在运行或可运行状态。
    • 黄色:表示线程处于活动状态,但它正在等待一项 I/O 操作(如磁盘或网络 I/O),然后才能完成它的工作。
    • 灰色:表示线程正在休眠且没有消耗任何 CPU 时间。当线程需要访问尚不可用的资源时,有时会发生这种情况。在这种情况下,要么线程自主进入休眠状态,要么内核将线程置于休眠状态,直到所需的资源可用。

    CPU Profiler 还会报告 Android Studio 和 Android 平台添加到应用进程的线程的 CPU 使用率,这些线程包括 JDWPProfile SaverStudio:VMStatsStudio:PerfaStudio:Heartbeat 等(然而,它们在线程活动时间轴上显示的确切名称可能有所不同)。Android Studio 将报告此数据,以便您确定线程活动和 CPU 使用率实际在何时是由您的应用的代码引发。

记录跟踪数据

要开始记录跟踪数据,请从 CPU Profiler 顶部的下拉菜单中选择记录配置,然后点击 Record

图 2. CPU Profiler 显示了正在进行的记录的状态、持续时间和类型。

与您的应用交互,然后在完成时点击 Stop。分析器将自动选择记录的时间范围,并在跟踪数据窗格中显示其跟踪信息,如图 3 所示。如果要检查其他线程的跟踪数据,请从线程活动时间轴上选择相应线程。

图 3. 记录方法跟踪数据后的 CPU Profiler。

  1. 选定范围:确定要在跟踪数据窗格中检查所记录时间的哪一部分。当您首次记录跟踪数据时,CPU Profiler 会自动在 CPU 时间轴上选择记录的完整长度。要仅检查所记录时间范围的一部分的跟踪数据,请拖动突出显示区域的边缘。
  2. 时间戳:指示所记录跟踪数据的开始和结束时间(相对于分析器开始收集 CPU 使用率信息的时间)。要选择完整的记录,请点击时间戳。
  3. 跟踪数据窗格:显示您选择的时间范围和线程的跟踪数据。只有在您至少记录一条跟踪数据后,才会显示此窗格。在此窗格中,您可以选择如何查看每个堆栈轨迹(使用跟踪数据标签),以及如何测量执行时间(使用时间参考下拉菜单)。
  4. 跟踪数据窗格标签:选择如何显示跟踪数据详细信息。如需详细了解各个选项,请参阅检查跟踪数据
  5. 时间参考菜单:选择以下选项之一,以确定如何测量每次调用的时间信息:
    • Wall clock time:时间信息表示实际经过的时间。
    • Thread time:时间信息表示实际经过的时间减去线程在该时间内没有消耗 CPU 资源的所有部分。对于任何给定的调用,其线程时间始终小于或等于其挂钟时间。使用线程时间可以让您更好地了解线程的实际 CPU 使用率中有多少是给定方法或函数消耗的。
  6. 过滤器:按函数、方法、类或软件包名称过滤跟踪数据。例如,如果您要快速识别与特定调用相关的跟踪数据,请点击 Filter 图标 ,或按 Ctrl + F 键(在 Mac 上,按 Command + F 键),然后在搜索字段中输入相应的名称。在 Call chartFlame chart 标签中,会突出显示包含符合搜索查询条件的调用、软件包或类的调用堆栈。在 Top downBottom up 标签中,这些调用堆栈优先于其他跟踪结果。您还可以通过勾选搜索字段旁边的相应框来启用以下选项:
    • Regex:要在您的搜索中包含正则表达式,请使用此选项。
    • Match case:如果您的搜索区分大小写,请使用此选项。

选择记录配置

在开始记录跟踪信息之前,请为要捕获的分析信息选择适当的记录配置:

  • 对 Java 方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈。分析器会比较捕获的数据集,以推导与应用的 Java 代码执行有关的时间和资源使用信息。

    基于采样的跟踪存在一个固有的问题,那就是如果应用在捕获调用堆栈后进入一个方法且在下次捕获前退出该方法,则分析器不会记录该方法调用。如果您想要跟踪生命周期如此短的方法,应使用检测跟踪。

  • 跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。

    请注意,与检测每个方法关联的开销会影响运行时性能,并且可能会影响分析数据;对于生命周期相对较短的方法,这一点更为明显。此外,如果应用在短时间内执行大量方法,则分析器可能很快就会超出其文件大小限制,因而不能再记录更多跟踪数据。

  • 对 C/C++ 函数采样:捕获应用的原生线程的采样跟踪数据。要使用此配置,您必须将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

    在内部,此配置使用 simpleperf 跟踪应用的原生代码。如果要为 simpleperf 指定其他选项,如对特定设备 CPU 采样或指定高精度采样持续时间,您可以从命令行使用 simpleperf

  • 跟踪系统调用:捕获精细的详细信息,以便您检查应用与系统资源的交互情况。您可以检查线程状态的确切时间和持续时间、直观地查看所有核心的 CPU 瓶颈在何处,并添加要分析的自定义跟踪事件。当您排查性能问题时,此类信息至关重要。要使用此配置,您必须将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

    使用此跟踪配置时,您可以通过检测代码来直观地标记分析器时间轴上的重要代码例程。要检测 C/C++ 代码,请使用 trace.h 提供的原生跟踪 API。要检测 Java 代码,请使用 Trace 类。如需了解详情,请参阅检测您的应用代码

    此跟踪配置在 systrace 的基础上构建而成。您可以使用 systrace 命令行实用程序指定 CPU Profiler 中提供的选项以外的其他选项。systrace 提供的其他系统级数据可帮助您检查原生系统进程并排查丢帧或帧延迟问题。

    在搭载 Android 9(API 级别 28)或更高版本的设备上,您可以使用一个名为“系统跟踪”的系统应用来记录设备上的系统跟踪数据

创建、修改或查看记录配置

您可以在 CPU Recording Configurations 对话框中创建、修改和查看记录配置,从 CPU Profiler 顶部的记录配置下拉菜单中选择 Edit configurations 即可打开该对话框。

要查看某个现有记录配置的设置,请在 CPU Recording Configurations 对话框的左侧窗格中选择该配置。

要创建一个新的记录配置,请执行以下操作:

  1. 点击对话框左上角的 Add 图标 。这样将创建一个包含一些默认设置的新配置。
  2. 为您的配置命名。
  3. 选择一个 Trace Technology
  4. 对于采样记录配置,以微秒 (μs) 为单位指定 Sampling interval。此值表示应用的每个调用堆栈样本的时间间隔。指定的时间间隔越短,达到记录数据的文件大小限制就越快。
  5. 对于写入连接设备的记录数据,以兆字节 (MB) 为单位指定 File size limit。当您停止记录时,Android Studio 会解析此数据并将其显示在分析器窗口中。因此,如果您增大此限制并记录大量的数据,Android Studio 解析文件所需的时间会大大增加,并且可能会变得无响应。

    注意:如果您使用的连接设备搭载的是 Android 8.0(API 级别 26)或更高版本,那么对跟踪数据的文件大小没有限制,系统会忽略此值。不过,您仍需留意每次记录后设备收集了多少数据,Android Studio 可能难以解析大型跟踪文件。例如,如果您记录的是采样时间间隔很短的采样跟踪数据,或是在应用短时间内调用许多方法的情况下记录检测跟踪数据,那么很快就会生成大型跟踪文件。

  6. 要接受所做的更改并继续对其他配置进行更改,请点击 Apply。要接受应用的所有更改并关闭对话框,请点击 OK

使用 Debug API 记录 CPU 活动

您可以使用 Debug API 让您的应用能够在 CPU Profiler 中开始和停止记录 CPU 活动。

当您的应用调用 startMethodTracing(String tracePath) 时,CPU Profiler 将开始记录;当您的应用调用 stopMethodTracing() 时,CPU Profiler 将停止记录。在记录使用此 API 触发的 CPU 活动时,CPU Profiler 会将 Debug API 显示为活动的 CPU 记录配置。

要使用 Debug API 控制 CPU 活动的记录,请将检测的应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

如需了解详情,请参阅通过检测您的应用生成跟踪日志

在应用启动过程中记录 CPU 活动

要在应用启动过程中自动开始记录 CPU 活动,请执行以下操作:

  1. 依次选择 Run > Edit Configurations
  2. Profiling 标签中,勾选 Start recording a method trace on startup 旁边的复选框。
  3. 从菜单中选择 CPU 记录配置。
  4. 点击 Apply
  5. 依次选择 Run > Profile,将您的应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

导出跟踪数据

使用 CPU Profiler 记录 CPU 活动后,您可以将相应数据导出为 .trace 文件,以便与他人共享或日后进行检查。

要从 CPU 时间轴导出跟踪文件,请执行以下操作:

  1. 在 CPU 时间轴上,右键点击要导出的记录的方法跟踪数据或系统跟踪数据。
  2. 从菜单中选择 Export trace
  3. 浏览到要将文件保存到的位置,指定文件名,然后点击 OK

要从 Sessions 窗格导出跟踪文件,请执行以下操作:

  1. Sessions 窗格中,右键点击要导出的记录的跟踪数据。
  2. 点击会话条目右侧的 Export method traceExport system trace 按钮。
  3. 浏览到要将文件保存到的位置,指定文件名,然后点击 OK

导入跟踪数据

您可以导入使用 Debug API 或 CPU Profiler 创建的 .trace 文件。

要导入跟踪文件,请点击分析器的 Sessions 窗格中的 Start new profiler session 图标 ,然后选择 Load from file

您可以检查 CPU Profiler 中导入的跟踪数据,就像检查直接在 CPU Profiler 中捕获的跟踪数据一样,但有下面几点不同:

  • CPU 活动未显示在 CPU 时间轴上。
  • 线程活动时间轴仅指明了可在哪里获取各线程的跟踪数据,而未指明实际线程状态(如运行中、等待中或休眠中)。

检查跟踪数据

CPU Profiler 中的跟踪数据窗格提供多个标签,供您选择如何查看所记录跟踪数据的信息。

对于方法跟踪数据和函数跟踪数据,您可以从 Call ChartFlame ChartTop DownBottom Up 标签中进行选择。对于系统跟踪数据,您可以从 Trace EventsFlame ChartTop DownBottom Up 标签中进行选择。

使用“Call Chart”标签检查跟踪数据

Call Chart 标签提供方法跟踪数据或函数跟踪数据的图形表示形式,其中调用的时间段和时间在水平轴上表示,而其被调用方显示在垂直轴上。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色。图 4 显示了一个调用图表示例,说明了给定方法或函数的 Self 时间、Children 时间和 Total 时间的概念。如需详细了解这些概念,请参阅有关如何使用 Top Down 和 Bottom Up 检查跟踪数据的部分。

图 4. 一个调用图表示例,说明了方法 D 的 Self 时间、Children 时间和 Total 时间。

提示:要跳转到某个方法或函数的源代码,请右键点击该方法或函数,然后选择 Jump to Source。从任何跟踪数据窗格标签中均可执行此操作。

使用“Flame Chart”标签检查跟踪数据

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

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

图 5. 一个调用图表,其中的多个方法调用有着共同的调用方顺序。

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

图 6. 汇总具有相同调用堆栈的完全相同的方法。

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

图 7. 图 5 中所示调用图表的火焰图表示形式。

使用“Top Down”和“Bottom Up”检查跟踪数据

Top Down 标签显示一个调用列表,在该列表中展开方法或函数节点会显示它的被调用方。图 8 显示了图 4 中调用图表的自上而下图。图中的每个箭头都从调用方指向被调用方。

如图 8 所示,在 Top Down 标签中展开方法 A 的节点会显示它的被调用方,即方法 B 和 D。在此之后,展开方法 D 的节点会显示它的被调用方,即方法 B 和 C,依此类推。与 Flame chart 标签类似,“Top Down”树也汇总具有相同调用堆栈的完全相同的方法的跟踪信息。也就是说,Flame chart 标签提供 Top down 标签的图形表示形式。

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

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

图 8. 一个“Top Down”树。

图 9. 图 8 中方法 C 的“Bottom Up”树。

Bottom Up 标签显示一个调用列表,在该列表中展开函数或方法节点会显示它的调用方。沿用图 8 中所示的跟踪数据示例,图 9 提供了方法 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 时间也可表示为在该记录期间占线程总时间的百分比。下表有助于说明如何解释顶部节点及其调用方(子节点)的时间信息。

Self Children Total
“Bottom Up”树顶部的方法或函数(顶部节点) 表示方法或函数在执行自己的代码(而非被调用方的代码)上所花的总时间。与“Top Down”树相比,此时间信息表示在记录的持续时间内对此方法或函数的所有调用的总和。 表示方法或函数在执行它的被调用方(而非自己的代码)上所花的总时间。与“Top Down”树相比,此时间信息表示在记录的持续时间内对此方法或函数的被调用方的所有调用的总和。 Self 时间和 Children 时间的总和。
调用方(子节点) 表示被调用方在由调用方调用时的总 Self 时间。以图 9 中的“Bottom Up”树为例,方法 B 的 Self 时间将等于每次执行由方法 B 调用的方法 C 所用的 Self 时间的总和。 表示被调用方在由调用方调用时的总 Children 时间。以图 9 中的“Bottom Up”树为例,方法 B 的 Children 时间将等于每次执行由方法 B 调用的方法 C 所用的 Children 时间的总和。 Self 时间和 Children 时间的总和。

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

使用“Trace Events”标签检查系统跟踪数据

检查系统跟踪数据时,您可以使用 Trace Events 标签查看每个线程上发生的事件的详细信息。

要查看某个线程的详细信息,请在 Threads 窗格中选择该线程。这样将在 Kernel 窗格中突出显示该线程在每个 CPU 核心上的活动,并在 Trace Events 标签中显示该线程的事件。在 Trace Events 标签中将鼠标指针悬停在某个事件上可查看该事件的名称以及在每种状态下所花的时间。

例如,在图 10 中,在 Threads 窗格中选择了 RenderThread,在 Kernel 窗格中突出显示了该线程在 CPU 0 和 CPU 1 上的活动,并在 Trace Events 标签中显示了在特定事件上所花的时间。

图 10. 查看渲染线程的 CPU 活动和跟踪事件。

如需详细了解如何检查系统跟踪信息,请参阅 systrace 文档的调查界面性能问题部分。

检查帧渲染数据

您可以检查应用在主线程和 RenderThread 上渲染每个帧所用的时间,以调查导致界面卡顿和帧速率较低的瓶颈。

要查看帧渲染数据,请使用可让您跟踪系统调用的配置记录跟踪数据。记录跟踪数据后,在名为 FRAMES 的部分下查找有关每个帧的信息,如图 11 所示。

图 11. 每个所用时间超过 16 毫秒的帧都以红色显示。