Android内存分析工具 — Memory Profiler

目录

前言

Android 存在内存回收机制,当它确定应用不再使用某些对象时,垃圾回收器会将未使用的内存释放回堆中。 虽然 Android 查找未使用内存的方式在不断改进,但对于所有 Android 版本,系统都必须在某个时间点短暂地暂停你写的代码。 大多数情况下,这些暂停难以察觉。 但是,如果你的应用分配内存的速度比系统回收内存的速度快,那么当释放足够的内存以满足应用的分配需要时,应用就可能出现延迟。 这样可能会导致应用跳帧,并使系统明显变慢

如果存在内存泄漏,则即使应用在后台运行也会保留该内存。 此行为会强制执行不必要的垃圾回收事件,因而拖慢系统的内存性能。 最后,系统被迫终止你的应用进程以回收内存。 然后,当用户返回你的应用时,就必须完全重启

为帮助防止这些问题,我们可以使用Memory Profiler

  • 实时图表展示应用内存使用量
  • 识别内存泄漏、抖动
  • 提供捕获堆转储、强制GC以及跟踪内存分配的能力

Memory Profiler 概览

图 1. Memory Profiler

如图 1 所示,Memory Profiler 的默认视图包括以下各项:

  1. 强制执行垃圾回收

  2. 堆转储,把内存信息通过文件的方式保存下来,可以进行分析

  3. 记录内存分配情况, 此按钮仅在连接至运行 Android 7.1 或更低版本的设备时才会显示

  4. 放大/缩小时间线

  5. 跳转至实时内存数据

  6. Event 时间线,显示 Activity 状态、用户输入 Event 和屏幕旋转 Event

  7. 内存使用量时间线,其包含以下内容:

    • 一个显示每个内存类别使用多少内存的堆叠图表,如左侧的 y 轴以及顶部的彩色键所示

    • 虚线表示分配的对象数,如右侧的 y 轴所示

    • 用于表示每个垃圾回收 Event 的图标

如何计算内存占用

图 2. Memory Profiler 顶部的内存计数图例

内存计数中的类别如下所示:

  • Java:从 Java 或 Kotlin 代码分配的对象内存

  • Native:从 C 或 C++ 代码分配的对象内存

    即使你的应用中不使用 C++,你也可能会看到此处使用的一些原生内存,因为 Android 框架使用原生内存代表您处理各种任务,如处理图像资源和其他图形时,即使你编写的代码采用 Java 或 Kotlin 语言

  • Graphics:图形缓冲区队列向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存 (请注意,这是与 CPU 共享的内存,不是 GPU 专用内存)

  • Stack: 应用中的原生堆栈和 Java 堆栈使用的内存。 这通常与您的应用运行多少线程有关

  • Code:应用用于处理代码和资源(如 dex 字节码、已优化或已编译的 dex 码、.so 库和字体)的内存

  • Other:应用使用的系统不确定如何分类的内存

  • Allocated:您的应用分配的 Java/Kotlin 对象数。 它没有计入 C 或 C++ 中分配的对象

    当连接至运行 Android 7.1 及更低版本的设备时,此分配仅在 Memory Profiler 连接至你运行的应用时才开始计数。 因此,你开始分析之前分配的任何对象都不会被计入。 不过,Android 8.0 附带一个设备内置分析工具,该工具可记录所有分配,因此,在 Android 8.0 及更高版本上,此数字始终表示你的应用中待处理的 Java 对象总数

Java 数字可能与你在 Android Monitor 中看到的数字并非完全相同,这是因为应用的 Java 堆是从 Zygote 启动的,而新数字则计入了为它分配的所有物理内存页面。 因此,它可以准确反映你的应用实际使用了多少物理内存

查看内存分配

要检查内存分配记录,可以按以下步骤操作:

  1. 浏览列表以查找堆计数异常大且可能存在泄漏的对象。 点击 Class Name 列标题以按字母顺序排序。 然后点击一个类名称。 此时在右侧将出现 Instance View 窗格,显示该类的每个实例,如图 3 中所示
  2. Instance View 窗格中,点击一个实例。 此时下方将出现 Call Stack 标签,显示该实例被分配到何处以及哪个线程中
  3. Call Stack 标签中,点击任意行以在编辑器中跳转到该代码
图 3. 有关每个已分配对象的详情显示在右侧的 **Instance View** 中

默认情况下,左侧的分配列表按类名称排列。 在列表顶部,你可以使用右侧的下拉列表在以下排列方式之间进行切换:

  • Arrange by class:基于类名称对所有分配进行分组
  • Arrange by package:基于软件包名称对所有分配进行分组
  • Arrange by callstack:将所有分配分组到其对应的调用堆栈

捕获堆转储

堆转储显示在您捕获堆转储时您的应用中哪些对象正在使用内存

要捕获堆转储,在 Memory Profiler 工具栏中点击 Dump Java heap

。 在转储堆期间,Java 内存量可能会暂时增加。因为堆转储与您的应用发生在同一进程中,并需要一些内存来收集数据

注:如果您需要更精确地了解转储的创建时间,可以通过调用 Debug.dumpHprofData() 在应用代码的关键点创建堆转储

要检查堆信息,请按以下步骤操作:

  1. 浏览列表以查找堆计数异常大且可能存在泄漏的对象。 为帮助查找已知类,点击 Class Name 列标题以按字母顺序排序。 然后点击一个类名称。 此时在右侧将出现 Instance View 窗格,显示该类的每个实例,如图 5 中所示
  2. Instance View窗格中,点击一个实例。此时下方将出现References,显示该对象的每个引用
  3. References 标签中,如果您发现某个引用可能在泄漏内存,则右键点击它并选择 Go to Instance

在堆转储中,请注意由下列任意情况引起的内存泄漏:

  • 长时间引用 ActivityContextViewDrawable 和其他对象,可能会保持对 ActivityContext 容器的引用
  • 可以保持 Activity 实例的非静态内部类,如 Runnable
  • 对象保持时间超出所需时间的缓存
图 4. 捕获堆转储需要的持续时间标示在时间线中

在类列表中,你可以查看以下信息:

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

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

  • Default heap:系统未指定堆时
  • App heap:应用在其中分配内存的主堆
  • Image heap:系统启动映像,包含启动期间预加载的类。 此处的分配保证绝不会移动或消失
  • Zygote heap:写时复制堆,其中的应用进程是从 Android 系统中派生的

Instance View 中,每个实例都包含以下信息:

  • Depth:从任意 GC 根到所选实例的最短 hop 数
  • Shallow Size:此实例的大小
  • Retained Size:此实例支配的内存大小

分析内存的技巧

使用 Memory Profiler 时,你可以应用代码施加压力并尝试强制内存泄漏。 在应用中引发内存泄漏的一种方式是,先让其运行一段时间,然后再检查堆。 泄漏在堆中可能逐渐汇聚到分配顶部。 不过,泄漏越小,你越需要运行更长时间的应用才能看到泄漏

您还可以通过以下方式之一触发内存泄漏:

  • 将设备从纵向旋转为横向,然后在不同的 Activity 状态下反复操作多次。 旋转设备经常会导致应用泄漏 ActivityContextView 对象,因为系统会重新创建 Activity,而如果您的应用在其他地方保持对这些对象之一的引用,系统将无法对其进行垃圾回收
  • 处于不同的 Activity 状态时,在您的应用与另一个应用之间切换(导航到主屏幕,然后返回到您的应用)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271