网站客服:zx81024163 |

Android内存泄漏检测工具使用手册

2023-10-21 15:03:37发布    浏览500次    信息编号:79723

友情提醒:凡是以各种理由向你收取费用,均有骗子嫌疑,请提高警惕,不要轻易支付。

Android内存泄漏检测工具使用手册

内存泄漏检测工具用户手册

前言

对于性能优化,除了我们平时的设计开发之外,还要考虑使用工具进行测试。 可以定位和分析问题的内存工具有很多,但并不是每个工具都能覆盖所有场景。

现在我就整理一些平时可以发现问题的简单工具的使用,并对这个ty页面进行内存泄漏分析。

public class LeakCanaryTestActivity extends BaseActivity {
    private static Test test;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        test = new Test(this);
    }
    private static class Test{
        public Test(Context context) {
            this.context = context;
        }
        private Context context;
        private int a;
        private int b;
    }
}

官方网站

原理很简单:or被销毁后,将它们的引用包装成1,然后将this与1关联起来。 检查它是否包含对 or 的引用。 如果没有触发GC,请再次检查。 如果仍然没有,则说明回收成功,否则可能发生泄漏。 这时候开始转储内存信息并分析泄漏的引用链。

访问于

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
}

在.0之前,我们访问时需要显式调用.(this); 在里面 。 启用内存监控的方法。

.0 启动通过自注册启用的内存监控。 我们在开发时通常使用的Run过程中也使用这种静默方式启动。

<provider android:name="com.android.tools.ir.server.InstantRunContentProvider" 
    android:multiprocess="true" 
  android:authorities="com.tzx.androidcode.com.android.tools.ir.server.InstantRunContentProvider"/>

内存泄漏分析

在进行调试或者UI自动化测试时,我们会在通知栏看到内存泄漏的提示。 查看详情后,我们可以看到相关内存泄漏的具体位置。 泄漏的成员变量用波浪线标记。

向服务器报告内存泄漏

升级到2.0的测试版和最终版本后,Shark官网文档提供的内存泄漏报告方法对应的API已经过时了。 我们需要实现一个新的接口来报告捕获的内存泄漏。

class LeakUploader : OnHeapAnalyzedListener {
  override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) {
    TODO("Upload heap analysis to server")
    //HeapAnalysis的toString和2.0之前的版本的LeakCanary.leakInfo获得的信息类似
    println(heapAnalysis)
  }
}
class MyApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    LeakCanary.config = LeakCanary.config.copy(
        onHeapAnalyzedListener = LeakUploader()
    )
  }
}

鲨鱼

鲨鱼官方网站

Shark是一个堆分析器,提供对2的支持。它是一个独立的堆分析库,可以高速运行,内存占用低(PS:2之前的堆分析库是哈哈,哈哈Git地址)。

这里提到的2个是测试版和最终版。 alpha版本还是用哈哈写的。

Shark 为 2 提供支持的同事也提供 Shark CLI 支持。

Shark 命令行界面 (CLI) 允许您直接从计算机分析堆。 它可以转储连接设备上安装的应用程序的堆,对其进行分析,甚至剥离所有敏感数据(例如 PII、密码或加密密钥)的堆转储,这在共享堆转储时非常有用。 有用。

Shark分析当前应用程序中的内存泄漏

shark-cli --device 设备id --process 包名 analyze

它还支持混淆的内存泄漏分析并使用文件进行可读性恢复。

shark-cli -d 设备id -p 包名 -m 混淆文件 analyze

Shark 分析 hprof 文件

shark-cli -h 生成的hprof文件 analyze

分为三大模块:CPU、内存、网络。

官网:用于查看Java堆和内存分配

是一个组件,可帮助您识别可能导致存根、冻结甚至应用程序崩溃的内存泄漏和内存溢出。 它显示应用程序内存使用情况的实时图表,让您可以捕获堆转储、强制垃圾回收并跟踪内存分配。

捕获堆转储进行分析

在列表顶部,您可以使用右侧的下拉菜单在列表之间切换:

查看heap dump后的信息:

垫安装

打开->帮助->,搜索安装,安装完成后重启。

垫的使用

将 dump heap 生成的 hprof 文件转换为 MAT 可以处理的 hprof 文件。

执行.os.Debug.()生成hprof文件。 记得执行前先进行GC。

hprof-conv 位于 sdk/-tools/hprof-conv 中。

hprof-conv memory-android.hprof memory-mat.hprof

MAT处理导入hprof文件

有以下几种观点:

查看含义

列出内存中对象的数量和大小,以及它们的名称

树

从对象的角度查看它们的内存状态

顶部

该视图显示了可能的内存泄漏点

检测多个类加载器加载的类

查找泄漏内存的类

根据内存中该类的对象实例的数量,判断该类的对象是否泄漏。

我们可以使用提供的各种搜索方法来搜索目标类。 我在这里使用包名称作为搜索元素。

尺寸

尺寸

大小=当前对象大小+当前对象可以直接或间接引用的对象大小之和。 (间接引用的含义:A->B->C,C是间接引用,如果B和C没有被其他对象引用,那么-A = (A + B + C) 类似)

换句话说,Size是当前对象被GC后可以从Heap释放的总内存。

但GC Roots直接或间接引用的对象在释放时必须排除。 暂时不会将它们视为此类。

从上图可以看出,ty和ty$a都有一个实例没有被回收。

分析泄漏类的引用关系

选择没有被回收的类,执行list -> with 操作,获取引用的对象。

with:该对象内部引用了哪些其他对象;

with:谁在引用这个对象;

获取到引用的类后,执行Path To GC Roots -> all /weak/soft等操作即可获取所有引用类型的引用。

(强引用):通常我们写的代码对应的是强可达性。 只有去掉强可达性才能回收对象。

(软引用):只要有足够的内存,就会一直保留该对象,直到发现内存紧张,没有更多的内存为止。 一般可以用来实现缓存。 当需要获取对象时,可以调用get方法。

(弱引用):随时可能被垃圾收集器回收,不一定要等到虚拟机内存不足才强制回收。 当你想获取一个对象时,也可以调用get方法。

(Dummy ):内存中根本没有对象保存,你只能使用它自己。 一般用于进入()方法后执行特殊的清理过程。

找到最后的泄漏点

从这个图中我们可以得到:

ty的一个实例由其内部类ty$Test的成员变量保存; ty$Test 的实例由 ty 的成员变量 test 保存。 合并比较分析

如果我们没有明确的目标类,我们可以比较两个 hprof 文件(泄漏之前,泄漏之后)。

选择泄漏前的hprof文件进行比较。

比较实例对象数量的增加和减少。 如上图所示,比较结果是ty和ty$a(这里的a是混淆后的Test)分别加1。

让我们继续上面相同的 MAT 分析步骤:

执行list->with操作;

执行 Path To GC Roots -> all /weak/soft 等操作;

最终的结果和之前的分析是一样的。

Jhat-Java自带的性能监控工具

Java8 jhat Java 堆文档

JHat是一款Hprof推出的分析软件。 它和MAT并称为Java内存静态分析工具。 与MAT的单用户界面分析不同,jHat采用多人界面分析。 它内置于 JDK 中。 在命令行输入jhat命令,查看是否有对应的命令。

➜  Desktop jhat
ERROR: No arguments supplied
Usage:  jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>
	-J<flag>          Pass <flag> directly to the runtime system. For
			  example, -J-mx512m to use a maximum heap size of 512MB
	-stack false:     Turn off tracking object allocation call stack.
	-refs false:      Turn off tracking of references to objects
	-port <port>:     Set the port for the HTTP server.  Defaults to 7000
	-exclude <file>:  Specify a file that lists data members that should
			  be excluded from the reachableFrom query.
	-baseline <file>: Specify a baseline object dump.  Objects in
			  both heap dumps with the same ID and same class will
			  be marked as not being "new".
	-debug <int>:     Set debug level.
			    0:  No debug output
			    1:  Debug hprof file parsing
			    2:  Debug hprof file parsing, no server
	-version          Report version number
	-h|-help          Print this help and exit
	<file>            The file to read
For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#" to the file name, i.e. "foo.hprof#3".
All boolean options default to "true"

Jhat使用的hprof文件需要使用hprof-conv进行hprof转换,就像MAT一样。

使用Jhat分析hprof文件后,会给出一个端口,例如7000。然后我们可以访问:7000/来查看分析结果。

所有类都显示在包中。 如果我们向下滚动到底部,我们可以看到其他查询方法。

显示堆

我们可以看到对应类的内存实例数量以及对应的占用内存大小。

:7000/历史/

查询(OQL)查询

可以使用OQL查询~!

OQL查询语法与VM的OQL类似~基本语法如下:

 select <JavaScript expression to select>
         [ from [instanceof] <class name> <identifier>
         [ where <JavaScript boolean expression to filter> ] ]

我们点击某个班级后,可以看到该班级的详细信息:

首先查看类的实例,然后查看每个实例的相关引用。

系统是基于Linux内核的操作系统,所以Linux中查看内存使用情况的命令在手机上也可以使用,比如top命令。

一般来说,内存使用有以下规则:VSS >= RSS >= PSS >= USS

缩写和全名含义相同。

电压信号

设置尺寸

虚拟内存消耗

(包括共享库占用的内存)是单个进程可以访问的地址空间

RSS

设置尺寸

实际物理内存使用情况

(包括共享库占用的内存)是单个进程实际占用的内存大小。 对于单个共享库来说,尽管无论有多少个进程使用它,共享库实际上只会被加载到内存中一次。

PSS

设置尺寸

实际使用的物理内存

(按比例分配共享库占用的内存)

美国海军

设置尺寸

进程单独占用的物理内存

(不包括共享库占用的内存)USS是一个非常非常有用的数字,因为它揭示了运行特定进程的内存的真实增量大小。 如果进程终止,USS 是实际返回给系统的内存量。

USS 是检测进程何时开始泄漏可疑内存的最佳数字。 如果怀疑程序存在内存泄漏,可以检查该值是否不断增加。

最后一个是本文叙述的重点。

dumpsys [options]
               meminfo 显示内存信息
               cpuinfo 显示CPU信息
               account 显示accounts信息
               activity 显示所有的activities的信息
               window 显示键盘,窗口和它们的关系
               wifi 显示wifi信息

用于查看内存信息,可以添加pid| 稍后查看应用程序的内存信息。

~/Desktop adb shell dumpsys meminfo com.tzx.androidcode
Applications Memory Usage (in Kilobytes):
Uptime: 131873995 Realtime: 240892295
** MEMINFO in pid 19924 [com.tzx.androidcode] **
                   Pss  Private  Private  SwapPss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    11062    11032        0       99    38912    21656    17255
  Dalvik Heap     4079     3984        0        0     5638     2819     2819
 Dalvik Other     1405     1404        0        1
        Stack       64       64        0        0
       Ashmem        2        0        0        0
      Gfx dev     2052     2052        0        0
    Other dev        8        0        8        0
     .so mmap      959       80       64        4
    .jar mmap     1062        0       24        0
    .apk mmap      109        0        0        0
    .ttf mmap       33        0        0        0
    .dex mmap     4513       36     4392        0
    .oat mmap      197        0        0        0
    .art mmap     6229     5924       16        1
   Other mmap      680      196       96        0
   EGL mtrack    19320    19320        0        0
    GL mtrack     6392     6392        0        0
      Unknown     1106     1092        0       15
        TOTAL    59392    51576     4600      120    44550    24475    20074
 App Summary
                       Pss(KB)
                        ------
           Java Heap:     9924
         Native Heap:    11032
                Code:     4596
               Stack:       64
            Graphics:    27764
       Private Other:     2796
              System:     3216
               TOTAL:    59392       TOTAL SWAP PSS:      120
 Objects
               Views:       82         ViewRootImpl:        2
         AppContexts:        8           Activities:        2
              Assets:       11        AssetManagers:        0
       Local Binders:       22        Proxy Binders:       41
       Parcel memory:       10         Parcel count:       24
    Death Recipients:        2      OpenSSL Sockets:        0
            WebViews:        0
 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

程序存储器分为2部分:和虚拟机。 虚拟机就是我们通常所说的java堆。 我们创建的对象是在里面分配的,但是直接在它上面分配的。 内存限制为+,不能超过最大限制。 从上面的信息中,可以看到应用程序占用的总量,这对于分析内存泄漏和内存溢出非常有用。

读取垃圾收集消息(GC Log)日志消息

在(非 ART)中,每个 GC 输出以下信息:

D/dalvikvm(PID): GC_Reason Amount_freed, Heap_stats, External_memory_stats, Pause_time

例子:

D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms

ART 日志消息

与 ART 不同,ART 不会记录未明确请求的 GC 消息。 仅当系统认为GC很慢时才会输出GC消息。 相反,仅当 GC 暂停时间超过 5 ms 或 GC 持续时间超过 100 ms 时。 如果应用程序不处于暂停明显的状态(例如当应用程序在后台运行时,在这种情况下 GC 暂停对用户来说是不可见的),则其所有 GC 都不被认为是慢的。 系统始终记录显式 GC。

ART 在其垃圾收集日志消息中包含以下信息:

I/art: GC_Reason GC_Name Objects_freed(Size_freed) AllocSpace Objects,
        Large_objects_freed(Large_object_size_freed) Heap_stats LOS objects, Pause_time(s)

例子:

I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects,
        21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms

这就是本文的全部内容。 如果您还有什么需要沟通的,请留言! !

如果您想阅读作者更多文章,可以查看我的个人博客和公众号:

提醒:请联系我时一定说明是从星空网上看到的!