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
这就是本文的全部内容。 如果您还有什么需要沟通的,请留言! !
如果您想阅读作者更多文章,可以查看我的个人博客和公众号:
提醒:请联系我时一定说明是从星空网上看到的!