Android studio 内存泄漏

    泄漏的源头有很多,有开源的第三方框架引起的、android系统自身造成的如webview的内存泄漏(需要在Activity销毁是一同销毁),还有一个是我们可以控制的就是自身编码引起的内存泄漏,这也是我们可以避免的。

    内存泄漏产生原因,一般有以下几种:

  1. static变量引发的内存泄漏
    static变量在程序启动开始加载,程序结束、死亡时释放,在static中被引用,会跟随static的生命周期,一直无法被释放。
    解决方法:合适时机把静态变量置空。
  2. 线程造成的内存泄漏
    线程或者Runnable是Acticvity内部类,因此握有Activity的实例(因为创建内部类必须依靠外部类),因此造成Activity无法释放。 AsyncTask 有线程池,问题更严重。
    解决方式:当Activity销毁的时候,对不需要的匿名内部类进行任务停止操作。
  3. Context 引起的内存泄漏
    在引用context的时候最好使用ApplicationContext,可以有效避免内存泄漏。
  4. BitMap占用太多内存
    bitmap解析需要占用内存,如果图片过多,并且没有解释recycle bitmap就会造成内存溢出
    解决方式:及时recycle压缩图片后加载图片
  5. 广播,监听等引起的
    在使用系统服务的时候,在使用完后需要把它释放掉(广播,监听等)。

实战篇(转载)

    Android Profiler:Android Studio 自带的分析工具


Android Profiler分为三大模块: cpu、内存 、网络。基本的使用在上一篇文章有讲到。这里详细说一下。

一、 CPU分析器CPU Profiler

CPU分析器可帮助您实时检查应用程序的CPU使用情况和线程活动,并记录方法跟踪,以便您可以优化和调试应用程序的代码。

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

  • 点击 View > Tool Windows > Android Profiler (还可以点击工具栏的image).
  • 从Android Profiler工具栏中选择要配置的设备和应用程序进程(如果您已通过USB连接设备但未看到它,请确保已启用USB调试)
  • 单击CPU时间轴中的任意位置打开CPU Profiler。

1.1 为什么要优化CPU的使用

优化CPU使用率有许多优点,例如提供更快更流畅的用户体验,并保持设备电池寿命。它还可以帮助您的应用程序在各种较新旧的设备上运行良好,您可以使用CPU分析器在与应用程序交互时监视CPU使用情况和线程活动,但是,有关应用程序执行代码的更详细信息,应记录并检查方法跟踪。

对于应用程序进程中的每个线程,您可以找到在一段时间内执行哪些方法以及每个方法在执行期间消耗的CPU资源。您还可以使用方法跟踪来识别调用者和被调用者,调用者是一种调用另一种方法的方法,被调用方是另一种方法调用的方法。您可以使用此信息来确定哪些方法太频繁地调用特定资源繁重的任务,就可以尝试优化应用程序的代码以避免不必要的工作。

如果要收集详细的系统级数据,帮助您检查本地系统进程并解决由丢帧引起的UI jank,则应使用Systrace。或者,如果要导出使用Debug捕获的.trace文件,则应使用Traceview

1.2 CPU Profiler概述

当您打开CPU分析器时,它会立即开始显示应用程序的CPU使用情况和线程活动。你会看到类似于下图的内容 
image

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

  • ①Event timeline: 显示您的应用程序在其生命周期中转换不同状态的活动,并指示用户与设备的交互,包括屏幕旋转事件。要了解有关事件时间轴的更多信息,包括如何启用它,请阅读我上一篇文章说到的启用高级分析
  • ②CPU timeline: 显示您的应用程序的实时CPU使用率(占总可用CPU的百分比)以及应用程序使用的线程总数,时间轴还显示其他进程的CPU使用情况(如系统进程或其他应用程序),所以您可以将其与应用程序的使用情况进行比较。您可以通过沿着时间轴的水平轴移动鼠标来检查历史CPU使用率数据。
  • ③Thread activity timeline: 列出属于您的应用程序进程的每个线程,并使用不同的颜色在时间轴上指示其活动。记录方法跟踪后,可以从此时间轴中选择一个线程,在跟踪窗格中检查其数据。 
    • 绿色: 线程处于活动状态或准备好使用CPU。也就是说,它处于”运行”或”可运行”状态。
    • 黄色: 线程处于活动状态,但是在完成其工作之前,它正在等待I / O操作(如文件或网络I / O)。
    • 灰色: 线程正在睡眠,不会消耗任何CPU时间,当线程需要访问尚未可用的资源时,有时会发生这种情况。要么线程进入自愿性睡眠,要么内核使线程休眠,直到所需的资源可用。
  • ④Tracing type:允许您选择以下选项之一来确定分析器如何记录方法跟踪。 
    • Sampled: 在应用程序执行期间,您可以频繁地捕获应用程序的调用堆栈。profiler将捕获的数据集进行比较,以获取关于应用程序代码执行的时间和资源使用信息。基于sampled跟踪的一个固有问题是,如果您的应用程序在捕获调用堆栈并在下一次捕获之前退出该方法,那么该方法调用不会被分析器记录。如果您对具有这样短生命周期的跟踪方法感兴趣,您应该使用工具跟踪。
    • Instrumented: 在您的应用程序运行时记录每个方法调用的开始和结束时的时间戳。收集时间戳并与生成方法跟踪数据进行比较,包括时间信息和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使用实际上是由你的应用程序代码引起的。

1.3 记录和检查方法跟踪

要开始记录方法跟踪,从下拉菜单中选择SampledInstrumented类型,然后单击Record开始进行记录,完成后点击Stop recording停止记录。profiler自动选择记录的时间帧,并在方法跟踪窗格中显示它的跟踪信息,如下图所示。如果要检查不同线程的方法跟踪,只需从线程活动时间轴中选择它。 
image

  • ① Selected time frame: 在跟踪窗格中检查的记录时间框架的部分。当您第一次记录一个方法跟踪时,CPU分析器将自动选择您在CPU时间线中记录的整个长度。如果要检查仅记录的时间帧的一部分的方法跟踪数据,您可以单击并拖动高亮显示区域的边缘来修改它的长度。
  • ②Timestamp: 表示记录方法跟踪的开始和结束时间(相对于profiler开始从设备收集CPU使用信息时)。你可以点击时间戳来自动选择整个记录作为你选定的时间框架——如果你有多个你想要转换的记录,这是非常有用的。
  • ③Trace pane:显示您所选择的时间框架和线程的方法跟踪数据。仅当您记录至少一个方法跟踪后,此窗格才会显示。在此窗格中,您可以选择如何查看每个堆栈跟踪(使用跟踪选项卡)以及如何测量执行时间(使用时间参考下拉菜单)。
  • ④: 选择显示为Top Down tree, Bottom Up tree, Call Chart, or Flame Chart这些类型的图。您可以在下面的部分中了解有关每个跟踪窗格选项卡的更多信息。
  • 从下拉菜单中选择以下选项之一,以确定如何测量每个方法调用的时序信息: 
    • Wall clock time: 表示实际经过时间。
    • Thread time:计时信息表示实际的消耗时间减去不消耗CPU资源的那段时间的任何部分。对于任何给定的方法,它的线程时间总是小于或等于它的时钟时间。使用线程时间让您更好地了解给定方法所消耗的线程实际CPU使用量
1.3.1 使用Call Chart选项卡检查跟踪

Call Chart选项卡提供一个方法跟踪的图形表示,其中一个方法调用(或调用者)的周期和时间在水平轴上表示,而它的callees则显示在垂直轴上。对系统api的方法调用以橙色显示,调用您的应用程序自己的方法以绿色显示,方法调用第三方api(包括java语言api)以蓝色显示。下面的图显示了一个示例调用图,并说明了给定方法的自时间、子时间和总时间的概念。关于如何使用自上而下和自下而上检查痕迹的部分,请继续看下去

图3

提示: 如果想要跳转到方法的源代码,请右键单击该方法,然后选择Jump to Source。这可以从任何窗格选项卡工作。

1.3.2 使用火焰图表(Flame Chart)选项卡检查痕迹

火焰图选项卡提供了一个反向调用图表,聚合了相同的调用堆栈。也就是说,收集相同的调用序列的相同方法被收集并表示为火焰图中的一个较长的栏(而不是将它们显示为多个更短的条,如调用图所示)。这样就更容易看出哪些方法消耗的时间最多。然而,这也意味着横轴不再表示时间轴,相反,它表示每个方法执行的相对时间。

为了帮助说明这个概念,考虑下面图4中的调用图表。注意,方法D对B(B1、B2和B3)进行多次调用,其中一些调用B对C(C1和C3)进行调用。

image

因为B1、B2和B3共享相同的序列调用者(A→D→B)聚合,如下所示。同样,C1和C3聚合,因为它们共享相同的序列调用者(A→D→B→C)注意不包括C2,因为它有不同的调用者序列(A→D→C)。

image

聚合方法调用用于创建flame 图,如下图所示。注意,对于任何给定的方法调用,在flame图中,消耗最多CPU时间的callees首先出现。 
image

1.3.3 使用自上而下和自下而上检查

Top Down选项卡显示方法调用的列表,扩展方法节点显示其callees。下图显示了上面的图3中调用图的顶部向下图。图中的每个箭头都是从调用者到callee。

下图所示,在顶部的down选项卡中扩展方法A的节点将显示它的callees、方法B和D。在此之后,扩展方法D的节点将暴露它的callees、方法B和C,等等。与火焰图选项卡类似,顶部向下的树聚合跟踪信息,用于共享相同调用堆栈的相同方法。也就是说,火焰图标签提供了顶部下标签的图形表示。

Top Down选项卡提供以下信息,以帮助描述在每个方法调用上花费的CPU时间(在选定的时间段内,时间也代表线程总时间的百分比):

  • Self:方法调用用于执行自己的代码而不是它的callees的时间量,如上面的图3所示。
  • Children:方法调用花费的时间用于执行其被调用者,而不是其自己的代码,如图3中的方法D所示。
  • Total:方法的Self和Children的时间的总和。这表示应用程序执行方法调用的总时间量,如图3所示的方法D。 
    image

Bottom Up选项卡显示一个方法调用列表,扩展方法的节点显示其调用者。使用上图所示的例子中,下图提供了一个自下而上方法C .在自下而上的树中打开方法C的节点,显示每个独特的调用者,方法B和d .注意,虽然B两次调用C,B当扩大节点只出现一次自下而上方法C的树。再此之后,展开节点B显示其调用者方法A和D.

image

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用于计时信息。

二、 内存分析器memory profiler

内存分析器是Android Profiler中的一个组件,它可以帮助您识别内存泄漏和内存溢出,从而导致存根、冻结甚至应用程序崩溃。它显示了应用程序内存使用的实时图,让您捕获堆转储、强制垃圾收集和跟踪内存分配。

要打开内存分析器和cpu检查器一样,就在隔壁。

2.1 为什么使用内存分析器

Android提供了一个托管内存环境——当它确定你的应用不再使用某些对象时,垃圾收集器会将未使用的内存释放回堆。在所有Android版本的某个点上,系统必须短暂地暂停代码。大多数时候,停顿是不可察觉的。但是,如果你的应用程序分配内存的速度快于系统收集的速度,你的应用程序可能会被延迟,而收集器释放了足够的内存来满足你的分配。延迟可能会导致应用程序跳过帧并导致明显的慢速。

即使你的应用程序没有表现出缓慢,如果它泄露了内存,它仍然可以保留那个内存,即使它在后台。通过强制不必要的垃圾收集事件,这种行为可以降低系统内存性能的其他部分。最终,系统不得不杀死你的应用程序来回收内存。然后当用户返回到你的应用程序时,它必须重新启动。

为了帮助防止这些问题,您应该使用内存分析器来执行以下操作:

  • 在可能导致性能问题的时间轴中寻找不良的内存分配模式
  • Dump Java堆,以便在任何时间查看哪些对象正在使用内存。长时间的堆转储可以帮助识别内存泄漏。
  • 在正常和极端的用户交互过程中记录内存分配,以精确地确定您的代码在短时间内分配的对象或分配被泄漏的对象。

有关可以减少应用程序内存使用的编程实践的信息,请参阅管理应用程序的内存。

2.2 内存分析器概述

image

如上图所示,内存分析器的默认视图包括以下内容:

  • ① 强制执行垃圾收集事件的按钮。
  • ② 捕获堆转储的按钮。
  • ③ 记录内存分配的按钮。
  • ④ 放大时间线的按钮。
  • ⑤ 跳转到实时内存数据的按钮。
  • ⑥ 事件时间线显示活动状态、用户输入事件和屏幕旋转事件。
  • ⑦ 内存使用时间表,其中包括以下内容: 
    • 每个内存类别使用多少内存的堆栈图,如左边的y轴和顶部的颜色键所示。
    • 虚线表示已分配对象的数量,如右侧y轴所示。
    • 每个垃圾收集事件的图标。

但是,默认情况下并不是所有的分析数据都可见。如果您看到一条消息,说“高级分析不可用于所选进程”,则需要启用高级分析以查看以下内容:

  • 活动时间表
  • 分配对象的数量
  • 垃圾收集事件

提示: 与之前的Android监控工具相比,新的内存分析器记录了你的内存使用情况,所以看起来你的内存使用量会更高。内存分析器监视一些额外的类别,这些类别增加了总数,但如果您只关心Java堆内存,那么“Java”的数字应该与上一个Android监视器的值类似。新的号码记录了从Zygote分派到应用程序的Java堆中的所有物理内存页面,这准确表示您的应用程序实际使用多少物理内存。

2.3 记录内存分配

查看堆转储时,查看分配了多少内存的快照很有用,它不会显示如何分配内存。为此,您需要记录内存分配。完成记录会话后,您可以看到以下记录的持续时间:

  • 分配了哪些对象以及它们使用了多少空间。
  • 在堆栈跟踪中分配每个对象的位置,其中包括线程。

要查看应用程序的内存分配,请单击内存分析器工具栏中的Record memory allocations。当它记录时,与你的应用程序进行交互,以引起内存溢出或内存泄漏。完成后,单击Stop recording

分配的对象列表出现在时间轴下面,按类名称分组,按堆计数排序,如上图所示。

分配跟踪器最多记录65535个分配。如果您的记录超出此限制,则只有最近65535个分配将保存在该记录中。

要检查分配记录,请按照下列步骤操作:

  • 浏览列表以查找具有非常大的堆计数且可能泄漏的对象,要帮助查找已知类,请单击类名列标题按字母顺序排序。然后单击一个类名,Instance View 窗格就会显示在右侧,显示该类的每个实例,如下图所示。
  • Instance View窗格中,单击一个实例。Call Stack选项卡显示在下面,显示了哪个实例被分配在哪个线程中。
  • Call Stack选项卡中,单击任意行可以在编辑器中跳转到该代码。

image

默认情况下,列表是按类名排列的。在列表的顶部,您可以使用右下拉菜单在列表之间切换:

  • Arrange by class: 根据类名分配。
  • Arrange by package:根据包名分配。
  • Arrange by callstack: 根据调用堆栈排序

2.4 捕获堆转储

堆转储显示在捕获堆转储时应用程序正在使用内存的对象。特别是在扩展用户会话之后,堆转储可以通过显示仍然在内存中的对象来帮助识别内存泄漏。捕获堆转储后,可以查看以下内容:

  • 您的应用程序分配了哪些类型的对象,以及每个对象的数量。
  • 每个对象使用多少内存
  • 每个对象的引用被保留在你的代码中。
  • 调用堆栈,用于分配对象的位置(只有在记录分配时捕获堆转储)。

要捕获堆转储,单击Memory-Profiler工具栏中的dump Java堆image。在转储堆时,Java内存的数量可能会暂时增加。这是正常的,因为堆转储发生在与应用程序相同的进程中,需要一些内存来收集数据。

堆转储出现在内存时间轴下方,显示堆中的所有类类型,如上图所示。

要检查你的堆,请按照下列步骤操作:

  • 浏览列表以查找具有异常大堆计数的对象,因为它可能会被泄露。为了帮助查找已知类,请单击类名列标题以按字母顺序排序。然后单击类名。实例视图窗格出现在右边,显示该类的每个实例,如下图所示。
  • Instance View窗格中,单击一个实例。 References选项卡显示在下面,显示对该对象的所有引用。或者单击实例名称旁边的箭头以查看其所有字段,然后单击字段名称以查看其所有引用。如果要查看某个字段的实例详细信息,请右键单击该字段,然后选择Go to Instance
  • References选项卡中,如果识别可能是内存泄漏的引用,请右键单击它,然后选择Go to Instance.。这将从堆转储中选择相应的实例,显示您自己的实例数据。

默认情况下,堆转储不会显示每个已分配对象的堆栈跟踪。要获取堆栈跟踪,您必须在单击转储Java堆之前开始记录内存分配。如果您这样做,您可以在实例视图中选择一个实例,并在References选项卡旁边看到Call Stack选项卡,如下图所示。但是,在开始记录分配之前,可能已经分配了一些对象,因此这些对象无法使用调用堆栈。包含一个调用堆栈的实例在图标上有一个stack标记image

在classes列表中,您可以看到以下信息:

  • Heap Count: 堆中的实例数。
  • Shallow Size: 此堆中所有实例的总大小(以字节为单位)。
  • Retained Size: 这个类的所有实例(以字节为单位)保留的内存总大小。

在类列表的顶部,可以使用左下拉列表在以下堆转储之间切换:

  • Default heap: 当系统没有指定堆时。
  • App heap: 应用程序分配内存的主堆。
  • Image heap: 系统引导映像,包含在引导期间预加载的类。这里的分配保证永远不会移动或离开。
  • Zygote heap: Android系统中分发应用程序进程的写时复制堆

默认情况下,列表按保留大小列排序。您可以单击任何列标题来更改列表的排序方式。

在Instance View中,每个实例包括以下内容:

  • Depth:从任何GC根到所选实例的跳数最短。
  • Shallow Size:此实例的大小。
  • Retained Size:此实例支配的内存大小(根据支配者树)。

三、 网络分析器(Network Profiler)

网络分析器在时间轴上显示实时网络活动,显示发送和接收的数据,以及当前连接的数量。这让您可以检查应用程序如何和何时传输数据,并适当地优化底层代码。

打开面板的步骤和上面的几乎一致。

3.1 为什么要使用网络分析器

当应用程序向网络发出请求时,设备必须使用耗电的移动或WiFi无线电来发送和接收数据包。接收器不仅使用电力传输数据,而且还使用额外的电源打开和保持唤醒。

使用网络分析器,您可以查找频繁的、短的网络活动高峰,这意味着您的应用程序要求网络经常打开,或者长时间保持唤醒,以处理许多短的请求。这一模式表明,您可以通过批处理网络请求来优化应用程序,以改善电池性能,从而减少网络必须打开或接收数据的次数。这也使得网络可以切换到低功率模式,以节省电池的时间间隔。

有关优化应用程序网络活动的技术的更多信息,请参阅 Reducing Network Battery Drain。

3.2 网络分析器概述

在窗口的顶部,您可以看到事件时间线和①无线电电源状态(high/low)和wi-fi。在时间轴上,您可以单击和拖动来选择②时间轴的一部分来检查流量。下面的③窗口显示在时间轴的选定部分中发送和接收的文件,包括文件名、大小、类型、状态和时间。您可以通过单击任何列标题来对列表进行排序。您还可以看到时间线所选部分的详细分解,显示每个文件被发送或接收的时间。

单击连接的名称,查看所选文件发送或接收的详细信息。单击④选项卡查看响应数据、头信息或调用堆栈。

image

提示:您必须启用高级概要分析来选择时间轴的一部分来检查,查看发送和接收的文件的列表,或者查看所选文件发送或接收的详细信息。为了启用高级分析,请查看上一篇文章

3.3 网络连接疑难解答

如果网络分析器检测到流量值,但无法识别任何支持的网络请求。您将收到以下错误消息:”Network Profiling Data Unavailable: There is no information for the network traffic you’ve selected.”

目前,网络分析器只支持HttpURLConnection和OkHttp库。如果您的应用程序使用另一个网络连接库,那么您可能无法在网络分析器中查看您的网络活动。如果您已经收到了这个错误消息,但是您的应用程序确实使用HttpURLConnection或OkHttp,请报告错误,以便我们可以调查这个问题。





你可能感兴趣的:(Android,Java,Android-Studio,profile,Android)