泄漏的源头有很多,有开源的第三方框架引起的、android系统自身造成的如webview的内存泄漏(需要在Activity销毁是一同销毁),还有一个是我们可以控制的就是自身编码引起的内存泄漏,这也是我们可以避免的。
内存泄漏产生原因,一般有以下几种:
实战篇(转载)
Android Profiler:Android Studio 自带的分析工具
Android Profiler分为三大模块: cpu、内存 、网络。基本的使用在上一篇文章有讲到。这里详细说一下。
CPU分析器可帮助您实时检查应用程序的CPU使用情况和线程活动,并记录方法跟踪,以便您可以优化和调试应用程序的代码。
要打开CPU Profiler,请按照下列步骤操作:
View > Tool Windows > Android Profiler
(还可以点击工具栏的).优化CPU使用率有许多优点,例如提供更快更流畅的用户体验,并保持设备电池寿命。它还可以帮助您的应用程序在各种较新旧的设备上运行良好,您可以使用CPU分析器在与应用程序交互时监视CPU使用情况和线程活动,但是,有关应用程序执行代码的更详细信息,应记录并检查方法跟踪。
对于应用程序进程中的每个线程,您可以找到在一段时间内执行哪些方法以及每个方法在执行期间消耗的CPU资源。您还可以使用方法跟踪来识别调用者和被调用者,调用者是一种调用另一种方法的方法,被调用方是另一种方法调用的方法。您可以使用此信息来确定哪些方法太频繁地调用特定资源繁重的任务,就可以尝试优化应用程序的代码以避免不必要的工作。
如果要收集详细的系统级数据,帮助您检查本地系统进程并解决由丢帧引起的UI jank,则应使用Systrace。或者,如果要导出使用Debug捕获的.trace文件,则应使用Traceview
当您打开CPU分析器时,它会立即开始显示应用程序的CPU使用情况和线程活动。你会看到类似于下图的内容
如上图所示,CPU Profiler的默认视图包括以下内容:
⑤Record button:开始和停止记录方法跟踪。要了解更多信息,请继续看下去
提示:profiler还报告了Android Studio和Android平台在你的应用程序过程中添加的线程的CPU使用情况,如JDWP、Profile Saver、Studio:VMStats、Studio:Perfa和Studio:Heartbeat(尽管,在线程活动时间线中显示的确切名称可能会有所不同)。这意味着您的应用程序在CPU时间轴上的CPU使用率也会报告这些线程使用的CPU时间。您可以在线程活动时间表中看到这些线程,并监视它们的活动。(但是,由于profiler线程执行native代码,因此无法为它们记录方法跟踪数据。)Android Studio会报告这些数据,这样你就可以很容易地识别出线程活动和CPU使用实际上是由你的应用程序代码引起的。
要开始记录方法跟踪,从下拉菜单中选择Sampled
或Instrumented
类型,然后单击Record
开始进行记录,完成后点击Stop recording
停止记录。profiler自动选择记录的时间帧,并在方法跟踪窗格中显示它的跟踪信息,如下图所示。如果要检查不同线程的方法跟踪,只需从线程活动时间轴中选择它。
Call Chart选项卡提供一个方法跟踪的图形表示,其中一个方法调用(或调用者)的周期和时间在水平轴上表示,而它的callees则显示在垂直轴上。对系统api的方法调用以橙色显示,调用您的应用程序自己的方法以绿色显示,方法调用第三方api(包括java语言api)以蓝色显示。下面的图显示了一个示例调用图,并说明了给定方法的自时间、子时间和总时间的概念。关于如何使用自上而下和自下而上检查痕迹的部分,请继续看下去
提示: 如果想要跳转到方法的源代码,请右键单击该方法,然后选择
Jump to Source
。这可以从任何窗格选项卡工作。
火焰图选项卡提供了一个反向调用图表,聚合了相同的调用堆栈。也就是说,收集相同的调用序列的相同方法被收集并表示为火焰图中的一个较长的栏(而不是将它们显示为多个更短的条,如调用图所示)。这样就更容易看出哪些方法消耗的时间最多。然而,这也意味着横轴不再表示时间轴,相反,它表示每个方法执行的相对时间。
为了帮助说明这个概念,考虑下面图4中的调用图表。注意,方法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)。
聚合方法调用用于创建flame 图,如下图所示。注意,对于任何给定的方法调用,在flame图中,消耗最多CPU时间的callees首先出现。
Top Down选项卡显示方法调用的列表,扩展方法节点显示其callees。下图显示了上面的图3中调用图的顶部向下图。图中的每个箭头都是从调用者到callee。
下图所示,在顶部的down选项卡中扩展方法A的节点将显示它的callees、方法B和D。在此之后,扩展方法D的节点将暴露它的callees、方法B和C,等等。与火焰图选项卡类似,顶部向下的树聚合跟踪信息,用于共享相同调用堆栈的相同方法。也就是说,火焰图标签提供了顶部下标签的图形表示。
Top Down选项卡提供以下信息,以帮助描述在每个方法调用上花费的CPU时间(在选定的时间段内,时间也代表线程总时间的百分比):
Bottom Up选项卡显示一个方法调用列表,扩展方法的节点显示其调用者。使用上图所示的例子中,下图提供了一个自下而上方法C .在自下而上的树中打开方法C的节点,显示每个独特的调用者,方法B和d .注意,虽然B两次调用C,B当扩大节点只出现一次自下而上方法C的树。再此之后,展开节点B显示其调用者方法A和D.
Bottom Up选项卡对于那些消耗最多(或最少)CPU时间的方法的排序方法很有用。您可以检查每个节点,以确定哪些调用者在调用这些方法上花费最多的CPU时间。与上面的树相比,底部树中每个方法的定时信息都是在每棵树的顶部(顶部节点)的方法。在记录期间,CPU时间也被表示为线程总时间的百分比。下表有助于解释如何解释顶级节点及其调用方方法(子节点)的定时信息。
名称 | Self | Children | Total |
---|---|---|---|
自下而上树顶部的方法(顶层节点) | 表示用于执行其自己的代码而不是其callees的方法的总时间。与上面的树相比,这个时间信息表示在记录期间对该方法的所有调用的总和。 | 表示用于执行callees而不是自己的代码的总时间。与上面的树相比,这个时间信息表示在记录期间对该方法的callees调用的所有调用的总和。 | Self时间和Children的时间总和 |
Caller 方法 (子节点) | 表示调用者调用callee的总时间。使用上图中的底向上树作为例子,方法B的自我时间将等于每个方法C调用时的Self时间的总和。 | 表示调用者调用的callee的总子时间。在上图中使用底部向上的树为例,方法B的孩子时间将等于每个方法C调用时执行方法C的总和。 | Self时间和Children的时间总和 |
对于给定的记录,当profiler达到文件大小限制时,Android Studio停止收集新数据(但是这并没有停止记录)。这种情况在执行检测跟踪时通常会发生得更快,因为这种类型的跟踪会在较短的时间内收集更多的数据,而不是取样跟踪。如果将检查时间帧扩展到在到达限制后发生的记录期间,那么跟踪窗格中的计时数据不会发生变化(因为没有可用的新数据)。此外,当您只选择没有可用数据的记录的部分时,跟踪窗格将显示NaN用于计时信息。
内存分析器是Android Profiler中的一个组件,它可以帮助您识别内存泄漏和内存溢出,从而导致存根、冻结甚至应用程序崩溃。它显示了应用程序内存使用的实时图,让您捕获堆转储、强制垃圾收集和跟踪内存分配。
要打开内存分析器和cpu检查器一样,就在隔壁。
Android提供了一个托管内存环境——当它确定你的应用不再使用某些对象时,垃圾收集器会将未使用的内存释放回堆。在所有Android版本的某个点上,系统必须短暂地暂停代码。大多数时候,停顿是不可察觉的。但是,如果你的应用程序分配内存的速度快于系统收集的速度,你的应用程序可能会被延迟,而收集器释放了足够的内存来满足你的分配。延迟可能会导致应用程序跳过帧并导致明显的慢速。
即使你的应用程序没有表现出缓慢,如果它泄露了内存,它仍然可以保留那个内存,即使它在后台。通过强制不必要的垃圾收集事件,这种行为可以降低系统内存性能的其他部分。最终,系统不得不杀死你的应用程序来回收内存。然后当用户返回到你的应用程序时,它必须重新启动。
为了帮助防止这些问题,您应该使用内存分析器来执行以下操作:
有关可以减少应用程序内存使用的编程实践的信息,请参阅管理应用程序的内存。
如上图所示,内存分析器的默认视图包括以下内容:
但是,默认情况下并不是所有的分析数据都可见。如果您看到一条消息,说“高级分析不可用于所选进程”,则需要启用高级分析以查看以下内容:
提示: 与之前的Android监控工具相比,新的内存分析器记录了你的内存使用情况,所以看起来你的内存使用量会更高。内存分析器监视一些额外的类别,这些类别增加了总数,但如果您只关心Java堆内存,那么“Java”的数字应该与上一个Android监视器的值类似。新的号码记录了从Zygote分派到应用程序的Java堆中的所有物理内存页面,这准确表示您的应用程序实际使用多少物理内存。
查看堆转储时,查看分配了多少内存的快照很有用,它不会显示如何分配内存。为此,您需要记录内存分配。完成记录会话后,您可以看到以下记录的持续时间:
要查看应用程序的内存分配,请单击内存分析器工具栏中的Record memory allocations
。当它记录时,与你的应用程序进行交互,以引起内存溢出或内存泄漏。完成后,单击Stop recording
。
分配的对象列表出现在时间轴下面,按类名称分组,按堆计数排序,如上图所示。
分配跟踪器最多记录65535个分配。如果您的记录超出此限制,则只有最近65535个分配将保存在该记录中。
要检查分配记录,请按照下列步骤操作:
Instance View
窗格中,单击一个实例。Call Stack
选项卡显示在下面,显示了哪个实例被分配在哪个线程中。Call Stack
选项卡中,单击任意行可以在编辑器中跳转到该代码。默认情况下,列表是按类名排列的。在列表的顶部,您可以使用右下拉菜单在列表之间切换:
堆转储显示在捕获堆转储时应用程序正在使用内存的对象。特别是在扩展用户会话之后,堆转储可以通过显示仍然在内存中的对象来帮助识别内存泄漏。捕获堆转储后,可以查看以下内容:
要捕获堆转储,单击Memory-Profiler工具栏中的dump Java堆。在转储堆时,Java内存的数量可能会暂时增加。这是正常的,因为堆转储发生在与应用程序相同的进程中,需要一些内存来收集数据。
堆转储出现在内存时间轴下方,显示堆中的所有类类型,如上图所示。
要检查你的堆,请按照下列步骤操作:
Instance View
窗格中,单击一个实例。 References
选项卡显示在下面,显示对该对象的所有引用。或者单击实例名称旁边的箭头以查看其所有字段,然后单击字段名称以查看其所有引用。如果要查看某个字段的实例详细信息,请右键单击该字段,然后选择Go to Instance
。References
选项卡中,如果识别可能是内存泄漏的引用,请右键单击它,然后选择Go to Instance.
。这将从堆转储中选择相应的实例,显示您自己的实例数据。默认情况下,堆转储不会显示每个已分配对象的堆栈跟踪。要获取堆栈跟踪,您必须在单击转储Java堆之前开始记录内存分配。如果您这样做,您可以在实例视图中选择一个实例,并在References选项卡旁边看到Call Stack选项卡,如下图所示。但是,在开始记录分配之前,可能已经分配了一些对象,因此这些对象无法使用调用堆栈。包含一个调用堆栈的实例在图标上有一个stack
标记。
在classes列表中,您可以看到以下信息:
在类列表的顶部,可以使用左下拉列表在以下堆转储之间切换:
默认情况下,列表按保留大小列排序。您可以单击任何列标题来更改列表的排序方式。
在Instance View中,每个实例包括以下内容:
网络分析器在时间轴上显示实时网络活动,显示发送和接收的数据,以及当前连接的数量。这让您可以检查应用程序如何和何时传输数据,并适当地优化底层代码。
打开面板的步骤和上面的几乎一致。
当应用程序向网络发出请求时,设备必须使用耗电的移动或WiFi无线电来发送和接收数据包。接收器不仅使用电力传输数据,而且还使用额外的电源打开和保持唤醒。
使用网络分析器,您可以查找频繁的、短的网络活动高峰,这意味着您的应用程序要求网络经常打开,或者长时间保持唤醒,以处理许多短的请求。这一模式表明,您可以通过批处理网络请求来优化应用程序,以改善电池性能,从而减少网络必须打开或接收数据的次数。这也使得网络可以切换到低功率模式,以节省电池的时间间隔。
有关优化应用程序网络活动的技术的更多信息,请参阅 Reducing Network Battery Drain。
在窗口的顶部,您可以看到事件时间线和①无线电电源状态(high/low)和wi-fi。在时间轴上,您可以单击和拖动来选择②时间轴的一部分来检查流量。下面的③窗口显示在时间轴的选定部分中发送和接收的文件,包括文件名、大小、类型、状态和时间。您可以通过单击任何列标题来对列表进行排序。您还可以看到时间线所选部分的详细分解,显示每个文件被发送或接收的时间。
单击连接的名称,查看所选文件发送或接收的详细信息。单击④选项卡查看响应数据、头信息或调用堆栈。
提示:您必须启用高级概要分析来选择时间轴的一部分来检查,查看发送和接收的文件的列表,或者查看所选文件发送或接收的详细信息。为了启用高级分析,请查看上一篇文章
如果网络分析器检测到流量值,但无法识别任何支持的网络请求。您将收到以下错误消息:”Network Profiling Data Unavailable: There is no information for the network traffic you’ve selected.”
目前,网络分析器只支持HttpURLConnection和OkHttp库。如果您的应用程序使用另一个网络连接库,那么您可能无法在网络分析器中查看您的网络活动。如果您已经收到了这个错误消息,但是您的应用程序确实使用HttpURLConnection或OkHttp,请报告错误,以便我们可以调查这个问题。