关于性能优化的问题,可能一直有遇到,也一直在解决,但是在中小厂和自我要求不严格的场景下,我们很少去实实在在的做性能优化;
在性能优化中我们通常会通过自己的经验或工具来发现问题,本想着能短时间内搞定Profiler工具,但是发现越看文档越多… 然后就过了这么多天了…
开篇前就说明一下,此blog会超级长,因为其覆盖面积较广,主要是讲解的详细,建议直接通过目录先看基本介绍,然后选取对应的检测点,我也不想这么长,主要是官网就这么长...
在开发中我时不时点一点Profiler
, 不过一直没去细究,但近期想继续提升一下自己 ~
故此,我抽时间对 Android Studio
内的 Profiler
总结了下心得(OS:在看过 官网 对Profiler
的文档,我都在怀疑有没有记录此篇blog的必要,不过最终还是动笔了,纯当二次解惑吧 ~)
Look Here:在套用官方文档的时候,我自己会做一些标注和记录 ~
正式开始前大家要记得 Profiler只是帮你分析哪里可能出了问题,哪里还可以优化,关于具体如何解决和优化还是要看自己的!
- 要好好掌握性能优化这个大方向 ~ ~
在开始之前,我一般喜欢问一下自己,为什么要学这个?学这个的意义是什么?要带着什么样的问题去找答案?
为什么要学这个?
为了优化程序,使程序更健壮,效率更高,性能优化是必不可少的过程
学这个的意义是什么?
最终目标是为了让用户有更好的体验
至于要带着什么样的问题去寻找答案,我会在对应的节点中去抛出我的问题
Profiler 性能分析工具 de 分析方向
Profiler 的俩种打开方式
View > Tool Windows > Profiler
点击工具栏中的 Profile 图标
开启
Profile
后效果借用官网的图直接讲一下Profiler界面元素
其实我觉得这部分大家闭着眼都知道是什么意思,还有怎么使用,不过官网介绍的更详细一些 ~ (下方数字对应图中的标记数字)
当前正在分析的进程和设备
。在 Sessions 窗格中,选择要查看的会话,或启动一个新的分析会话
。使用缩放按钮控制要查看的时间轴范
围,或使用 Attach to live 按钮跳转到实时更新
。事件时间轴显示与用户输入相关的事件,包括键盘活动、音量控制变化和屏幕旋转
。共享时间轴视图,包括 CPU、内存、网络和耗电量图表
。
性能分类切换按钮,包括:cpu、memeory、network、energy
。页面调整按钮集合,包括:缩小,放大、重置、暂停、开始
等。事件时间轴,显示Activity的生命周期不同状态,用户交互事件,如点击,旋转
等。网络计数图例:Receiving:接收到的数据包大小
Sending:发送的数据包大小
Connections:网络请求类型计数(同一个链接多次请求,按1计数)
网络图形化显示窗格。
CPU Profiler - CPU分析
当我看到
CPU Profiler
的第一时间,我首先想到了性能优化中的卡顿优化
,众所周知当fps<60时就会出现卡顿问题
,当然造成卡顿的原因往往意味着短时间内处理大量的事务
,但是当我们解决问题时,就需要定位具体的节点,所以往下看吧…
CPU 性能剖析器还会报告 Android Studio 和 Android 平台添加到应用进程的线程的 CPU 使用率
,这些线程包括 JDWP、Profile Saver、Studio:VMStats、Studio:Perfa 和 Studio:Heartbeat
等(不过,它们在线程活动时间轴上显示的确切名称可能有所不同)。
Android Studio 报告此数据是为了方便确定线程活动和 CPU 使用率什么时候是由您应用的代码实际引发的
。
如图所示,CPU 性能剖析器的默认视图包括以下时间轴:
1.事件时间轴:显示应用中的 Activity 在其生命周期内不断转换经历各种不同状态的过程,并指示用户与设备的交互,包括屏幕旋转事件。
注意
:如需了解如何在搭载 Android 7.1(API 级别 25)及更低版本的设备上启用事件时间轴,请参阅启用高级性能剖析。
2. CPU 时间轴:显示应用的实时 CPU 使用率
(以占总可用 CPU 时间的百分比表示)以及应用当前使用的线程总数
。此时间轴还会显示其他进程(如系统进程或其他应用)的 CPU 使用率
,以便您可以将其与您应用的 CPU 使用率进行对比
。您可以通过沿时间轴的横轴方向移动鼠标来检查历史 CPU 使用率数据。
3. 线程活动时间轴:列出属于应用进程的每个线程
,并使用下面列出的颜色在时间轴上指示它们的活动
。
记录跟踪数据后,您可以从此时间轴上选择一个线程,以在跟踪数据窗格中检查其数据。
线程处于正在运行或可运行状态
。黄色:表示线程处于活动状态
,但它正在等待一项 I/O *** 作(如磁盘或网络 I/O),然后才能完成它的工作。灰色:表示线程正在休眠且没有消耗任何 CPU 时间
。 当线程需要访问尚不可用的资源时,就会出现这种情况。在这种情况下,要么线程主动进入休眠状态,要么内核将线程置于休眠状态,直到所需的资源可用。
记录跟踪数据
主要以 Record - Stop 定义我们要分析的区间数据
如需开始记录跟踪数据
,请从 CPU 性能剖析器上方或下方的下拉菜单中选择记录配置,然后点击 Record
。
与您的应用交互,然后在完成时点击 Stop
。性能剖析器会自动在跟踪数据窗格中显示其跟踪信息,如图所示:
记录方法跟踪数据后的 CPU 性能剖析器
确定需在跟踪数据窗格中检查所记录时间的哪一部分
。当您首次记录跟踪数据时,CPU 性能剖析器会自动在 CPU 时间轴上选择记录的完整长度。如需仅检查已记录的时间范围中的一部分的跟踪数据,请拖动突出显示区域的边缘。"Interaction"部分:沿着时间轴显示用户互动和应用生命周期事件
。"Threads"部分:沿时间轴针对每一个线程显示线程状态活动(例如运行、休眠等)和调用图表
(在 System Trace 中则为跟踪事件图表)。– 使用鼠标和键盘快捷键在时间轴上导航。
– 双击线程名称,或在选中线程时按 Enter 键以展开或折叠线程。
– 选择某个线程即可在“Analysis”窗格中查看更多信息。 按住 Shift 或 Ctrl(Mac 上为 Command)可选择多个线程。
– 选择方法调用(或 System Trace 中的跟踪事件),以在“Analysis”窗格中查看更多信息。"Analysis"窗格:
显示您选择的时间范围和线程/方法调用的跟踪数据
。在此窗格中,您可以选择如何查看每个堆栈轨迹
(使用“Analysis”标签页),以及如何测量执行时间
(使用“Time reference”下拉菜单)。"Analysis"窗格标签页:选择如何显示跟踪数据详细信息
。如需详细了解各个选项,请参阅检查跟踪数据。"Time reference"菜单:选择以下选项之一,以确定如何测量每次调用的时间信息
(仅在示例/跟踪 Java 方法中受支持):– Wall clock time:该时间信息表示
实际经过的时间
。– Thread time:
该时间信息表示实际经过的时间减去线程没有占用 CPU 资源的那部分时间
。对于任何给定的调用,其线程时间始终小于或等于其挂钟时间。使用线程时间可以让您更好地了解线程的实际 CPU 使用率中有多少是给定方法或函数占用的。Filter:按函数、方法、类或软件包名称过滤跟踪数据
。例如,如果您需快速识别与特定调用相关的跟踪记录数据,请在搜索字段中输入名称。在 Flame chart
标签页中,会突出显示包含符合搜索查询条件的调用、软件包或类的调用堆栈。在 Top down
和 Bottom up
标签页中,这些调用堆栈优先于其他跟踪结果。您还可以通过勾选搜索字段旁边的相应方框来启用以下选项:– Regex:如需在您的搜索中包含正则表达式,请使用此选项。
– Match case:如果您的搜索区分大小写,请使用此选项。 选择记录配置
在开始记录跟踪信息之前,请为需捕获的分析信息选择适当的记录配置:
对 Java 方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈
。分析器会比较捕获的数据集,以推导与应用的 Java 代码执行有关的时间和资源使用信息。
基于采样的跟踪存在一个固有的问题,那就是如果应用在捕获调用堆栈后进入一个方法并在下次捕获前退出该方法,分析器将不会记录该方法调用。如果您想要跟踪生命周期如此短的方法,应使用插桩跟踪。
跟踪 Java 方法:在运行时检测应用,从而在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率
。
请注意,与检测每个方法相关的开销会影响运行时性能,并且可能会影响分析数据;对于生命周期相对较短的方法,这一点更为明显。此外,如果应用在短时间内执行大量方法,则分析器可能很快就会超出其文件大小限制,因而不能再记录更多跟踪数据。
对 C/C++ 函数采样:捕获应用的原生线程的采样跟踪数据
。如需使用此配置,您必须将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。
在内部,此配置使用 simpleperf 跟踪应用的原生代码。如果需为 simpleperf 指定其他选项,如对特定设备 CPU 采样或指定高精度采样持续时间,您可以从命令行使用 simpleperf。
跟踪系统调用:捕获非常翔实的细节,以便您检查应用与系统资源的交互情况
。您可以检查线程状态的确切时间和持续时间、直观地查看所有内核的 CPU 瓶颈在何处,并添加需分析的自定义跟踪事件。当您排查性能问题时,此类信息至关重要。如需使用此配置,您必须将应用部署到搭载 Android 7.0(API 级别 24)或更高版本的设备上。
使用此跟踪配置时,您可以通过检测代码,直观地标记性能剖析器时间轴上的重要代码例程。如需检测 C/C++ 代码,请使用由 trace.h 提供的原生跟踪 API。如需检测 Java 代码,请使用 Trace 类。如需了解详情,请参阅检测您的应用代码。
此跟踪配置建立在 systrace 的基础之上。您可以使用 systrace 命令行实用程序指定除 CPU 性能剖析器提供的选项之外的其他选项。systrace 提供的其他系统级数据可帮助您检查原生系统进程并排查丢帧或帧延迟问题。
在搭载 Android 9(API 级别 28)或更高版本的设备上,您可以使用一个名为“System Tracing”的系统应用来记录设备上的系统跟踪数据。
您可以在 CPU Recording Configurations 对话框中创建、修改和查看记录配置
,从 CPU 性能剖析器顶部的记录配置下拉菜单中选择 Edit configurations 即可打开该对话框。
如需查看某个现有记录配置的设置,请在 CPU Recording Configurations 对话框的左侧窗格中选择该配置。
如需创建一个新的记录配置,请执行以下 *** 作:
点击对话框左上角的 Add
图标 。这样会创建一个包含一些默认设置的新配置
。
为您的配置命名。
选择一种 Trace Technology。
对于采样记录配置,以微秒 (μs) 为单位指定 Sampling interval
。此值表示应用的每个调用堆栈样本的时间间隔。指定的时间间隔越短,达到记录数据的文件大小限制就越快。
对于写入连接设备的记录数据,以兆字节 (MB) 为单位指定 File size limit。当您停止记录时,Android Studio 会解析此数据并将其显示在分析器窗口中
。因此,如果您提高此限制并记录大量的数据,Android Studio 解析文件所需的时间会大大增加,并且可能会变得无响应。
– 注意:如果您使用的连接设备搭载的是 Android 8.0(API 级别 26)或更高版本,那么对跟踪数据的文件大小没有限制,系统会忽略此值。不过,您仍需留意每次记录后设备收集了多少数据,Android Studio 可能会无法解析大型跟踪文件。例如,如果您记录的是采样时间间隔很短的采样跟踪数据,或是在应用于短时间内调用许多方法的情况下记录检测跟踪数据,那么很快就会生成大型跟踪文件。
如需接受所做的更改并继续对其他配置进行更改,请点击 Apply。如需接受进行的所有更改并关闭对话框,请点击 OK。
使用 Debug API 记录 CPU 活动可以使用 Debug API,让应用能够在 CPU 性能剖析器中开始和停止记录 CPU 活动。
重要提示:Debug API 应该与用于开始和停止 CPU 活动记录的其他方法(如 CPU 性能剖析器图形界面中的按钮,以及记录配置中用于在应用启动时自动记录的设置)分开使用。
由于存在 8MB 的缓冲区空间限制,因此 Debug API 中的 startMethodTracing(String tracePath) 方法专为时间间隔较短或难以手动启动/停止记录的场景而设计。如需设置持续时间更长的记录,请使用 Android Studio 中的性能剖析器界面
当您的应用 调用
startMethodTracing(String tracePath)
时,CPU 性能剖析器将开始记录
;当您的应用调用
stopMethodTracing()
时,CPU 性能剖析器将停止记录
。
在记录使用此 API 触发的 CPU 活动时,CPU 性能剖析器会将 Debug API 显示为正在发挥作用的 CPU 记录配置。
如需使用 Debug API 控制 CPU 活动的记录,请将检测的应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。
如需了解详情,请参阅通过检测您的应用生成跟踪日志。
在应用启动过程中记录 CPU 活动如需在应用启动过程中自动开始记录 CPU 活动
,请执行以下 *** 作:
Profiling
标签中,勾选 Start recording a method trace on startup 旁边的复选框。从菜单中选择 CPU 记录配置。点击 Apply。依次选择 Run > Profile,将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备
。
检查跟踪数据
CPU 性能分析器中的轨迹视图提供了多种方法查看来自所记录的轨迹的信息。
对于方法轨迹和函数轨迹,您可以直接在 Threads 时间轴中查看 Call Chart,并从 Analysis 窗格中查看 Flame Chart、Top Down、Bottom Up 和 Events 标签页。对于系统轨迹,您可以直接在 Threads 时间轴中查看 Trace Events,并从 Analysis 窗格中查看 Flame Chart、Top Down、Bottom Up 和 Events 标签页。
使用鼠标和键盘快捷键可以更轻松地导航 Call Charts 或 Trace Events。
使用 Call Chart 检查跟踪数据提示:如需跳转到某个方法或函数的源代码,请右键点击该方法或函数,然后选择 Jump to Source。在“分析”窗格的任意标签页中都可以执行此 *** 作。
Call Chart 以图形方式来呈现方法跟踪数据或函数跟踪数据
,其中调用的时间段和时间在横轴上表示,而其被调用方则在纵轴上显示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色
。
下图显示了一个调用图表示例,说明了给定方法或函数的 Self 时间、Children 时间和 Total 时间的概念
。如需详细了解这些概念,请参阅有关如何使用"Top Down"和"Bottom Up"标签页检查跟踪数据的部分。
一个调用图表示例,说明了方法 D 的 Self 时间、Children 时间和 Total 时间。
使用"Flame Chart"标签检查跟踪数据Flame Chart 标签页提供一个倒置的调用图表,用来汇总完全相同的调用堆栈
。也就是说,将具有相同调用方顺序的完全相同的方法或函数收集起来,并在火焰图中将它们表示为一个较长的横条(而不是将它们显示为多个较短的横条,如调用图表中所示)。这样更方便您查看哪些方法或函数消耗的时间最多。不过,这也意味着,横轴不代表时间轴,而是表示执行每个方法或函数所需的相对时间。
为帮助说明此概念,不妨考虑下图(一个调用图表,其中的多个方法调用具有相同的调用方顺序)中的调用图表。请注意,方法 D 多次调用 B(B1、B2 和 B3),其中一些对 B 的调用也调用了 C(C1 和 C3)。
由于 B1、B2 和 B3 具有相同的调用方顺序 (A → D → B),因此系统将它们汇总在一起,如下图( 汇总具有相同调用堆栈的完全相同的方法)所示。同样,也将 C1 和 C3 汇总在一起,因为它们也具有相同的调用方顺序 (A → D → B → C)。请注意,C2 不包括在内,因为它具有不同的调用方顺序 (A → D → C)。
汇总的调用用于创建火焰图,如图(前面的图所示调用图表的火焰图表示形式)所示。请注意,对于火焰图中的任何给定调用,占用最多 CPU 时间的被调用方最先显示。
Top Down 标签显示一个调用列表,在该列表中展开方法或函数节点会显示它的被调用方。下图(“Top Down”树)显示了之前图中调用图表的自上而下图。图中的每个箭头都从调用方指向被调用方。
如下图所示,在 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 所示。上图中方法 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 时间也可表示为在该记录期间占线程总时间的百分比。下表说明了如何解读顶部节点及其调用方(子节点)的时间信息。
注意:对于给定的记录,当分析器达到文件大小限制时,Android Studio 会停止收集新数据(不过,不会停止记录)。在执行检测跟踪时,这种情况通常发生得更快,因为与采样跟踪相比,此类跟踪会在更短的时间内收集更多的数据。如果您将检查时间范围延长至达到限制后的记录时段,则跟踪数据窗格中的时间数据不会发生变化(因为没有新数据可用)。此外,当您仅选择没有可用数据的那部分记录时,对于时间信息,轨迹窗格将显示 NaN。
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 时间的总和。 |
“Events”表格列出了当前所选线程中的所有调用。您可以点击列标题对它们进行排序。通过选择表格中的某一行,您可以在时间轴上导航到所选调用的开始时间和结束时间。这样,您就可以在时间轴上准确定位事件。
查看“Analysis”窗格中的“Events”标签页
检查系统跟踪数据时,您可以在 Threads 时间轴中检查Trace Events
,以查看每个线程上所发生事件的详细信息。将鼠标指针悬停在某个事件上,可查看该事件的名称以及在每种状态下所花费的时间。点击事件可在 Analysis 窗格中查看详情。
除了 CPU 调度数据外,系统轨迹还包括按核心记录的 CPU 频率。它可以显示每个核心上的活动数量,让您可以了解哪些是新型移动处理器中的“大”核心或“小”核心。
查看渲染线程的 CPU 活动和轨迹事件
CPU Cores 窗格(如上图所示)显示每个核心上安排的线程活动。将鼠标指针悬停在某个线程活动上,可查看该核心在该特定时间在哪个线程上运行。
如需详细了解如何检查系统轨迹信息,请参阅 systrace 文档的调查界面性能问题部分。
检查系统轨迹:帧渲染时间轴您可以检查应用在主线程和 RenderThread 上渲染每个帧所用的时间,以调查造成界面卡顿和帧速率低的瓶颈。
如需查看帧渲染数据,请使用使您可以跟踪系统调用的配置来记录轨迹。记录轨迹后,在 Display 部分的 Frames 时间轴下查找有关每个帧的信息,如图 12 所示。
每个用时超过 16 毫秒的帧都以红色显示。
“Display”部分的详细视图
Display 部分中显示的跟踪记录如下:
对于部署到搭载 Android 9 或更高版本的设备的应用,Process Memory (RSS) 部分会显示该应用当前使用的物理内存量。
在性能分析器中查看物理内存
Total:这是您的进程当前使用的物理内存总量。在基于 Unix 的系统上,这被称为“驻留集大小”,是匿名分配、文件映射和共享内存分配所使用的所有内存的组合。
对于 Windows 开发者,驻留集大小类似于工作集大小。
Allocated:此计数器跟踪进程的正常内存分配目前占用了多少物理内存。这些分配均匿名(不由特定文件支持)且不公开(不共享)。在大多数应用中,这由堆分配量(使用 malloc 或 new)和堆栈内存组成。从物理内存中换出时,这些分配会写入系统交换文件。
File Mappings:此计数器会跟踪进程用于文件映射的物理内存量,也就是说,通过内存管理器从文件映射至内存区域的内存。
Shared:此计数器跟踪在此进程和系统中其他进程之间共享的内存所用的物理内存量。
导出跟踪数据在使用 CPU 性能剖析器记录 CPU 活动后,可以将 相应数据导出为 .trace 文件
,以便与他人共享或日后进行检查。
如需从 CPU 时间轴导出跟踪文件
,请执行以下 *** 作:
如需从Sessions 窗格导出跟踪文件
,请执行以下 *** 作:
支持导入使用 Debug API 或 CPU 性能剖析器创建的 .trace 文件。
导入跟踪文件方式:在性能剖析器的 Sessions
窗格中点击 Start new profiler session
图标 ,然后选择 Load from file
。
您可以检查导入到 CPU 性能剖析器中的跟踪数据,就像检查直接在 CPU 性能剖析器中捕获的跟踪数据一样,但有下面几点不同
:
CPU Activity 未显示在 CPU 时间轴上
(在 System Trace 中除外)。Threads 部分中的时间轴不会显示线程状态,如正在运行、等待或休眠
(在 System Trace 中除外)。
Memory Profiler - 内存分析
内存分析的最大作用应该就是判断内存的分配了吧?让我们看出内存的使用方,看出内存的使用率等等 ~
关于该节点,我有以下问题
内存开销的地方(都谁占了内存)?内存开销的大小(多少)?内存分配的流程、记录(整个内存分配的记录过程)?怎么检测内存有没有OOM?注意:使用搭载 Android 8.0(API 级别 26)及更高版本的设备时,Memory Profiler 还会显示应用中的一些误报的原生内存使用量,而这些内存实际上是分析工具使用的。对于大约 100000 个对象,最多会使报告的内存使用量增加 10MB。 在 IDE 的未来版本中,这些数字将从您的数据中过滤掉。
通过Profiler
进入到Memory Profiler
中,我依旧借用了官网的一张图来进行阐述 ~
强制执行垃圾回收事件
的按钮。(众所周知Android是gc自动回收垃圾的,有了这样的 *** 作相当于开启了快捷功能)用于捕获堆转储(后面有单独解释)
的按钮。
注意:只有在连接到搭载 Android 7.1(API 级别 25)或更低版本的设备时,系统才会在堆转储按钮右侧显示用于记录内存分配情况的按钮。
用于指定性能分析器多久捕获一次内存分配的下拉菜单。选择适当的选项可帮助您在进行性能剖析时提高应用性能。用于缩放时间轴的按钮。用于跳转到实时内存数据的按钮。事件时间轴,显示活动状态、用户输入事件和屏幕旋转事件。内存使用量时间轴,它会显示以下内容:一个堆叠图表,显示每个内存类别当前使用多少内存,如左侧的 y 轴以及顶部的彩色键所示。
一条虚线,表示分配的对象数,如右侧的 y 轴所示。
每个垃圾回收事件的图标。 计算内存
Look here: 在内存性能分析器顶部看到这样的一堆数字,主要基于应用提交的所有专用内存
此数据由 Android 系统根据其记录提供 - 该计数不包含与系统或其他应用共享的页面
我项目中自己选择某个时间后的内存数据状态
内存计数中的类别如下
即使您的应用中不使用 C++,您也可能会看到此处使用了一些原生内存,因为即使您编写的代码采用 Java 或 Kotlin 语言,Android 框架仍使用原生内存代表您处理各种任务,如处理图像资源和其他图形。Graphics:图形缓冲区队列为向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。(请注意,这是与 CPU 共享的内存,不是 GPU 专用内存。)Stack:您的应用中的原生堆栈和 Java 堆栈使用的内存。这通常与您的应用运行多少线程有关。Code:您的应用用于处理代码和资源(如 dex 字节码、经过优化或编译的 dex 代码、.so 库和字体)的内存。Others:您的应用使用的系统不确定如何分类的内存。Allocated:您的应用分配的 Java/Kotlin 对象数。此数字没有计入 C 或 C++ 中分配的对象。
如果连接到搭载 Android 7.1 及更低版本的设备,只有在内存性能分析器连接到您运行的应用时,才开始此分配计数。因此,您开始分析之前分配的任何对象都不会被计入。但是,Android 8.0 及更高版本附带一个设备内置性能剖析工具,该工具可跟踪所有分配,因此,在 Android 8.0 及更高版本上,此数字始终表示您的应用中待处理的 Java 对象总数。
与以前的 Android Monitor 工具中的内存计数相比,新的内存性能分析器以不同的方式记录您的内存,因此,您的内存使用量现在看上去可能会更高些。内存性能分析器会监控一些额外的类别,这就增加了总的内存使用量,但如果您仅关心 Java 堆内存,“Java”项的数字应与以前工具中的数值相似。 然而,Java 数字可能与您在 Android Monitor 中看到的数字并非完全相同,这是因为新数字计入了自应用的 Java 堆从 Zygote 派生以来为其分配的所有物理内存页面。因此,它可以准确反映您的应用实际使用了多少物理内存。
查看内存分配情况在了解
Memory Profiler
的基本概念后,我们接下来的重点就是掌握如何实时获取内存的分配情况
内存分配情况图表显示内存中每个 Java 对象
和 JNI 引用
的分配方式
。具体而言,内存性能分析器可为您显示有关对象分配情况的以下信息:
关于内存分配方面,不同的版本对应的查询内存分配的方式有所不同
,主要分化在俩个版本间,其一Android 8.0 或更高版
,其二Android 7.1 或更低版本
~
搭载Android 8.0 或更高版本,可以随时查看对象分配
具体 *** 作步骤如下:在时间轴上拖动以选择要查看哪个区域的分配(Google动态视频)。不需要开始记录会话,因为 Android 8.0 及更高版本附带设备内置分析工具,可持续跟踪应用分配。
搭载 Android 7.1 或更低版本
请点击内存性能分析器工具栏中的 Record memory allocations
图标 。记录时,内存性能分析器会跟踪您的应用中发生的所有分配。完成后,请点击 Stop recording 图标
就可以查看具体分配了;(Google动态视频)查看具体分配过程。
注意:在 Android 7.1 及更低版本上,您最多可以记录 65535 个分配。 如果您的记录会话超出此限制,记录中仅保存最新的 65535 个分配。(在 Android 8.0 及更高版本上,则没有实际的限制。)
选择时间轴的某个区域后(或者使用搭载 Android 7.1 或更低版本的设备完成记录会话后),已分配对象的列表将显示在时间轴下方,按类名称进行分组,并按其堆计数排序。
如果有那种专业精神的人,肯定喜欢一路钻进去,这里主要记录如何内存的分配过程,Monmey Profiler 也提供了查看方式 ~
请按以下步骤 *** 作:
浏览列表以查找堆计数异常大且可能存在泄漏的对象。为帮助查找已知类,点击Class Name
列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View
窗格,显示该类的每个实例。
此外,您也可以快速找到对象,方法是点击 Filter 图标 ,或按 Ctrl+F 键(在 Mac 上,按 Command+F 键),然后在搜索字段中输入类或软件包名称。如果从下拉菜单中选择 Arrange by callstack,还可以按方法名称搜索。如需使用正则表达式,请勾选 Regex 旁边的复选框。如果您的搜索查询区分大小写,请勾选 Match case 旁边的复选框。
在Instance View
窗格中,点击一个实例。此时下方将出现 Call Stack
标签页,显示该实例被分配到何处以及在哪个线程中。在 Call Stack
标签页中,右键点击任意行并选择 Jump to Source
,以在编辑器中打开该代码。
可以使用已分配对象列表上方的两个菜单选择需检查的堆以及如何组织数据
从左侧的菜单中,选择需检查的堆:
default heap:当系统未指定堆时。image heap:系统启动映像,包含启动期间预加载的类。此处的分配确保绝不会移动或消失。zygote heap:写时复制堆,其中的应用进程是从 Android 系统中派生的。app heap:您的应用在其中分配内存的主堆。JNI heap:显示 Java 原生接口 (JNI) 引用被分配和释放到什么位置的堆。从右侧的菜单中,选择如何安排分配:
Arrange by class:根据类名称对所有分配进行分组。这是默认值。Arrange by package:根据软件包名称对所有分配进行分组。Arrange by callstack:将所有分配分组到其对应的调用堆栈 查看全局 JNI 引用Java 原生接口 (JNI) 是一个允许 Java 代码和原生代码相互调用的框架。
其实关于JNI,我很少使用到... 但不影响我们学习 ~
JNI 引用由原生代码进行管理,因此原生代码使用的 Java 对象可能会保持活动状态过长时间。如果丢弃了 JNI 引用而未先明确将其删除,Java 堆上的某些对象可能会变得无法访问。此外,还可能会达到全局 JNI 引用限制。
如需排查此类问题,请使用内存性能分析器中的 JNI heap 视图浏览所有全局 JNI 引用
,并按 Java 类型和原生调用堆栈对其进行过滤。
借助此信息,您可以了解创建和删除全局 JNI 引用的时间和位置
。
在您的应用运行时,选择您要检查的一部分时间轴,然后从类列表上方的下拉菜单中选择 JNI heap
。 您随后可以像往常一样检查堆中的对象,还可以双击 Allocation Call Stack
标签页中的对象,以查看在代码中将 JNI 引用分配和释放到了什么位置
如需检查应用的 JNI 代码的内存分配,您必须将应用部署到搭载 Android 8.0 或更高版本的设备上。
如需详细了解 JNI,请参阅 JNI 提示
注意:原生内存性能分析器提供的内存数据不同于 Java 堆的内存性能分析器提供的数据。 原生内存性能分析器只会跟踪通过 C/C++ 分配器(包括原生 JNI 对象)进行的分配,而不会对 Java 堆上的对象进行性能剖析。
原生内存性能分析器基于性能分析工具的 Perfetto 堆栈中的 heapprofd 构建。如需详细了解原生内存性能分析器的内部信息,请参阅 heapprofd 文档。
注意:自最初的 Android Studio 4.1 版其,原生内存分析器在应用启动期间会停用。 此选项会在即将发布的版本中启用。
解决方法之一是,您可以使用 Perfetto 独立命令行性能剖析器捕获启动配置文件。
Android Studio 内存性能分析器包含原生内存性能分析器
,该工具适用于部署到搭载 Android 10 的物理设备的应用
;Android Studio4.2 预览版现提供对 Android 11 设备的支持
。
原生内存性能分析器会跟踪特定时间段内采用原生代码表示的对象的分配/解除分配情况,并提供以下信息:
Allocations:在选定时间段内通过 malloc() 或 new 运算符分配的对象数。Deallocations:在选定时间段内通过 free() 或 delete 运算符解除分配的对象数。Allocations Size:在选定时间段内所有分配的总大小(以字节为单位)。Deallocations Size:在选定时间段内所有已释放内存的总大小(以字节为单位)。Total Count:Allocations 列中的值减去 Deallocations 列中的值所得的结果。Remaining Size:Allocations Size 列中的值减去 Deallocations Size 列中的值所得的结果。如需启动记录,请点击内存性能分析器窗口顶部的
Record native allocations
:当您准备好完成录制时,请点击 Stop recording。
默认情况下,原生内存性能分析器使用的示例大小为 32 个字节:每次分配 32 个字节的内存时,系统都会截取内存的快照。示例规模越小,快照越频繁,从而产生更准确的内存用量数据。示例规模越大,数据的准确性越差,但占用的系统资源越少,因此录制时的性能就越高。
如需更改原生内存性能分析器的示例大小,请执行以下 *** 作:
依次选择Run > Edit Configurations。
在左侧窗格中选择您的应用模块。点击 Profiling
标签页,然后在标记为原生内存采样间隔(字节
)的字段中输入示例大小。再次构建并运行您的应用。
捕获堆转储
堆转储显示在您捕获堆转储时您的应用中哪些对象正在使用内存。特别是在长时间的用户会话后,堆转储会显示您认为不应再位于内存中却仍在内存中的对象,从而帮助识别内存泄漏。
捕获堆转储后,您可以查看以下信息:
您的应用分配了哪些类型的对象,以及每种对象有多少。每个对象当前使用多少内存。在代码中的什么位置保持着对每个对象的引用。对象所分配到的调用堆栈。(目前,对于 Android 7.1 及更低版本,只有在记录分配期间捕获堆转储时,才会显示调用堆栈的堆转储。)如需捕获堆转储,请点击内存性能分析器工具栏中的 Dump Java heap 图标 。 在转储堆期间,Java 内存量可能会暂时增加。 这很正常,因为堆转储与您的应用发生在同一进程中,并需要一些内存以收集数据。
堆转储出现在内存时间轴下方,显示堆中的所有类类型,如图 5 所示。
如果您需要更确切地了解转储的创建时间,可以通过调用 dumpHprofData() 在应用代码的关键点创建堆转储。
在类列表中,您可以查看以下信息:
Allocations:堆中的分配数。Native Size:此对象类型使用的原生内存总量(以字节为单位)。只有在使用 Android 7.0 及更高版本时,才会看到此列。您会在此处看到采用 Java 分配的某些对象的内存,因为 Android 对某些框架类(如 Bitmap)使用原生内存。
Shallow Size:此对象类型使用的 Java 内存总量(以字节为单位)。Retained Size:为此类的所有实例而保留的内存总大小(以字节为单位)。您可以使用已分配对象列表上方的两个菜单选择要检查的堆转储以及如何组织数据。
从左侧的菜单中,选择需检查的堆
:
从右侧的菜单中,选择如何安排分配
:
默认情况下,此列表按 Retained Size 列排序。如需按其他列中的值排序,请点击该列的标题。
点击一个类名称可在右侧打开 Instance View
窗口(如下图所示)。列出的每个实例都包含以下信息:
注意:默认情况下,堆转储不会向您显示每个已分配对象的堆栈轨迹。如需获取堆栈轨迹,在点击 Dump Java heap 之前,您必须先开始记录内存分配。然后,您可以在 Instance View 中选择一个实例,并查看 References 标签页旁边的 Call Stack 标签页,如图 6 所示。不过,在您开始记录分配之前,可能已分配一些对象,因此不会显示这些对象的调用堆栈。 包含调用堆栈的实例在图标 上用一个“堆栈”标志表示。 (遗憾的是,由于堆栈轨迹需要您执行分配记录,因此您目前无法在 Android 8.0 上查看堆转储的堆栈轨迹。)
如需检查您的堆,请按以下步骤 *** 作:
1.浏览列表以查找堆计数异常大且可能存在泄漏的对象。为帮助查找已知类,点击 Class Name
列标题以按字母顺序排序。然后,点击一个类名称。此时右侧将出现 Instance View
窗格,显示该类的每个实例,如下图所示。
此外,您也可以快速找到对象,方法是点击 Filter 图标 ,或按 Ctrl+F 键(在 Mac 上,按 Command+F 键),然后在搜索字段中输入类或软件包名称。如果从下拉菜单中选择 Arrange by callstack
,还可以按方法名称搜索。如需使用正则表达式,请勾选 Regex
旁边的复选框。如果您的搜索查询区分大小写,请勾选 Match case
旁边的复选框。
在 Instance View
窗格中,点击一个实例。此时下方将出现 References
标签页,显示对该对象的每个引用。
或者,点击实例名称旁边的箭头以查看其所有字段,然后点击一个字段名称以查看其所有引用。如需查看某个字段的实例详细信息,请右键点击该字段并选择 Go to Instance
。
在 References
标签页中,如果您发现某个引用可能在泄漏内存,请右键点击它并选择 Go to Instance
。这样会从堆转储中选择相应的实例,从而向您显示它自己的实例数据。
在您的堆转储中,请注意由下列任意情况引起的内存泄漏:
长时间引用 Activity、Context、View、Drawable 和其他对象,可能会保持对 Activity 或 Context 容器的引用。可以保持 Activity 实例的非静态内部类,如 Runnable。对象保持时间比所需时间长的缓存。 将堆转储另存为 HPROF 文件捕获堆转储后,只有在内存性能分析器正在运行时,才能在该分析器中查看数据。当您退出分析会话时,会丢失堆转储。因此,如果您要保存堆转储以供日后查看,请将其导出到 HPROF 文件。在 Android Studio 3.1 及更低版本中,Export capture to file 按钮 位于时间轴下方工具栏的左侧;在 Android Studio 3.2 及更高版本中,Sessions 窗格中每个 Heap Dump 条目的右侧都有一个 Export Heap Dump 按钮。在随即显示的 Export As 对话框中,使用 .hprof 文件扩展名保存文件。
如需使用其他 HPROF 分析器(如 jhat),您需要将 HPROF 文件从 Android 格式转换为 Java SE HPROF 格式。 您可以使用 android_sdk/platform-tools/ 目录中提供的 hprof-conv 工具执行此 *** 作。运行包含两个参数(即原始 HPROF 文件和转换后 HPROF 文件的写入位置)的 hprof-conv 命令。例如:
hprof-conv heap-original.hprof heap-converted.hprof
导入堆转储文件
如需导入一个HPROF (.hprof)
文件,请点击 Sessions
窗格中的 Start a new profiling session
图标 ,选择 Load from file
,然后从文件浏览器中选择该文件。
您还可以通过将 HPROF 文件从文件浏览器拖动到编辑器窗口导入该文件。
内存性能分析器中的泄漏检测在内存性能分析器中分析堆转储时,您可以过滤 Android Studio 认为可能表明应用中的 Activity 和 Fragment 实例存在内存泄漏的分析数据。
过滤器显示的数据类型包括:
已销毁但仍被引用的 Activity 实例。没有有效的 FragmentManager 但仍被引用的 Fragment 实例。在某些情况(如以下情况)下,过滤器可能会产生误报:
已创建 Fragment,但尚未使用它。正在缓存 Fragment,但它不是 FragmentTransaction 的一部分。如需使用此功能,请先捕获堆转储或将堆转储文件导入 Android Studio。如需显示可能泄漏内存的 Fragment 和 Activity,请勾选内存性能分析器的堆转储窗格中的 Activity/Fragment Leaks 复选框,如下图所示。
使用内存性能分析器时,您应对应用代码施加压力并尝试强制内存泄漏。在应用中引发内存泄漏的一种方式是,先让其运行一段时间,然后再检查堆。泄漏在堆中可能逐渐汇聚到分配顶部。不过,泄漏越小,为了发现泄漏而需要运行应用的时间就越长。
您还可以通过以下某种方式触发内存泄漏:
在不同的 Activity 状态下,先将设备从纵向旋转为横向,再将其旋转回来,这样反复旋转多次。旋转设备经常会使应用泄漏 Activity、Context 或 View 对象,因为系统会重新创建 Activity,而如果您的应用在其他地方保持对这些对象其中一个的引用,系统将无法对其进行垃圾回收。在不同的 Activity 状态下,在您的应用与其他应用之间切换(导航到主屏幕,然后返回到您的应用)。提示:您还可以使用 monkeyrunner 测试框架来执行上述步骤。
Network Profiler - 网络分析
故名思意,网络分析就是查看应用当前的网络相关事件,包含请求报文、响应报文,对于每个请求,具体到检查大小、类型、状态和传输时长等等 ~
我们通过 双击
进入到 NetWork Profiler
内部查看详情
下图是我所遇到的问题,完全无法使用 Network Profiler
的功能 …
通过查询官方文档后找到了不能使用 Nerwork Profiler 的原因
:目前,网络性能剖析器仅支持
HttpURLConnection 和 OkHttp 网络连接库。
Network Profiling Data Unavailable
: There is no information for the network traffic you've selected.
我们还是以官网能正常使用 Network Profile
的场景来进行继续学习
Google
将 Network Profiler 分为三部分
第一部分:窗口顶部显示事件时间轴
。在时间轴 (1) 上,可以点击并拖动以选择时间轴的一部分来检查网络流量。(可以选择某个峰值,也可以区间选择
)
第二部分:在时间轴下方的窗格 (2) 中可以选择Connection View、Thread View标签页
,以详细了解时间轴上选定时段内的网络活动
Connection View
:列出了在时间轴上选定时段内从应用的所有 CPU 线程发送或接收的文件。对于每个请求,您可以检查大小、类型、状态和传输时长。 您可以通过点击任意列标题来对此列表排序。您还会看到时间轴上选定时段的明细数据,从而了解每个文件的发送或接收时间。Thread View
:显示您应用的每个 CPU 线程的网络活动。 如下图所示,您可以在此视图中检查各网络请求由应用的哪些线程负责。第三部分:从 Connection View 或 Thread View
中点击请求名称,可检查有关已发送或已接收数据的详细信息
(3)。
点击各个标签页可查看响应标头和正文、请求标头和正文或调用堆栈。
在 Response 和 Request 标签页
中,点击 View Parsed
链接可显示格式化文本
,点击 View Source
链接可显示原始文本
。
Evengy Profiler - 能耗分析
在我理解能耗分析,一般都体验在电量方面,主要减少用户的用电量,而造成电量损耗的方面太多了,例如网络请求
当您打开能耗性能剖析器时,它会立即开始显示应用的估算耗电量。系统会显示类似于图 1 的界面。
如图 1 所示,能耗性能剖析器的默认视图包括以下时间轴:
“Event”时间轴
:显示应用中的 Activity 在其生命周期内不断转换而经历各种不同状态的过程。此时间轴还会指示用户与设备的交互,包括屏幕旋转事件。“Energy”时间轴
:显示应用的估算耗电量。“System”时间轴
:显示可能会影响耗电量的系统事件。如需查看 CPU、网络和位置信息 (GPS) 资源,以及相关系统事件的具体耗电量情况,请将鼠标指针放在 Energy 时间轴中的条形上方。
检查系统事件:唤醒锁定、作业和闹钟
您可以使用能耗性能剖析器查找可能会影响耗电量的系统事件,包括唤醒锁定、作业和闹钟:
唤醒锁定是一种机制,可在设备进入休眠模式时使 CPU 或屏幕保持开启状态。例如,播放视频的应用可以使用唤醒锁定,以便在用户未与设备交互时使屏幕保持开启状态。请求唤醒锁定不是一项耗电量很高的 *** 作,但未撤消唤醒锁定会导致屏幕或 CPU 保持开启状态的时间超过必要时间,从而加快电池耗电速度。如需了解详情,请参阅有关使用唤醒锁定的指南。
您可以使用闹钟定期在应用上下文之外运行后台任务。当闹钟触发时,它可能会唤醒设备并运行耗电量很高的代码。如需了解详情,请参阅有关使用闹钟的指南。
您可以使用作业在指定条件下(例如恢复网络连接时)执行相关 *** 作。您可以使用 JobBuilder 创建作业,并使用 JobScheduler 对这些作业进行调度。在许多情况下,建议您使用 JobScheduler 对作业进行调度,而不是使用闹钟或唤醒锁定。
位置信息请求使用 GPS 传感器,这会消耗大量电量。如需了解如何在发出位置信息请求时节省电量,请参阅优化电池的位置。
借助能耗性能剖析器,您可以轻松找到应用使用各项功能的位置,以便您就如何使用各项功能做出明智的决策。
能耗性能剖析器会在 Energy 时间轴下的 System 时间轴中显示一个彩色编码的条形,以表示系统事件处于活动状态的时间范围。唤醒锁定用红色条形表示,作业和闹钟用黄色条形表示,位置信息事件用浅紫色条形表示。
下图 显示了能耗性能剖析器,并在代码编辑器中定位到了未释放唤醒锁定对应的源代码。
System Event
窗格并显示唤醒锁定等事件的详细信息,请在 Energy
时间轴中选择一个时间范围。如需打开 Wake Lock Details
窗格并显示特定唤醒锁定的详细信息,请在 System Event
窗格中选择该唤醒锁定。如需打开代码编辑器并跳转到唤醒锁定的源代码,请在 Wake Lock Details
窗格中双击调用堆栈顶部的调用方法条目。用于获取唤醒锁定的调用会在源代码编辑器中突出显示。
有关显示其他系统事件详情的说明与唤醒锁定基本相同,只不过对应的详细信息窗格中提供了特定于各种事件的信息。例如,Job Details 窗格会显示调度作业以及完成作业的代码部分的调用堆栈。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)