Android 应用内存分析(一)

一 :通过 adb shell dumpsys meminfo ${PROCESS_NAME} 查看内存情况

这里我们选取手机上的一个应用Phoneix查看一下其内存占用情况: 执行命令:

adb shell dumpsys meminfo com.trassion.phoneix 

显示结果如下:

image.png

在上图中,我们可以看到App Summary部分,就是通过Memory Profiler界面上查看到的数值。 那么,问题来了,App Summary部分又是怎么计算得来的呢?

在上图中,我们注意到了App Summary上边的第一个表格的数据,莫非是根据这一部分计算来的吗? 答案是Yes,下面我们给出详细的计算规则:

Java Heap = Dalvik Heap private dirty+ .art mmap private (clean+ dirty) = 27412 + 4 + 7824 = 35240

Native Heap = Native private dirty = 98156

Code = .so mmap private (clean + dirty) + .jar mmap private (clean + dirty) + .apk mmap private (clean + dirty) + .ttf mmap private (clean+ dirty) + .dex mmap private (clean + dirty) + .oat mmap private (clean + dirty) = (520 + 2348) + (8 + 500) + (60 + 1764) + (0 + 48) + (15808 + 2316) + (0 + 0) = 23440

Stack = Stack private dirty = 6144 

Graphics = GL mtrack private (clean + dirty) + EGL mtrack private(clean + dirty) = (0 + 0) + (28769 + 0) = 28769

Private other= TOTAL private (clean + dirty) - Java Heap - Native Heap- Code- Stack -Graphic = (205245 + 7780) - 35240 - 98156 - 23440 - 6144 - 28769 = 21276

System = TOTAL - TOTAL private (clean + dirty)  = 309121 - (205245 + 7780) = 96096


  • Private Dirty 进程本身使用的内存总数,包含了进程主动申请的以及修改的继承自Zygote的内存。其实Private Dirty表示了该进程私有的,不跟Disk数据一致的内存段。例如堆(heap),栈(stack),bss段。

注:在新平台上,用于管理Dalvik的内存(如, just-in-time compilation (JIT) and GC bookkeeping)不再像以前一样归到 Dalvik Heap,而是归类到 Dalvik Other。

  • Private clean 进程独自使用的so和dex。Clean内存的好处是在内存紧张时,可以释放物理内存。因为是clean的,所以不需要写回到disk,只需要下次读取该内存(导致缺页错误)时再从disk读入。

  • Heap Size/ Heap Alloc/ Heap Free Heap Alloc是(Dalvik、native)app申请的内存记录,包括了Private Dirty和继承自Zygote的(多进程共享的)内存。所以,它是比Pss Total和Private Dirty都要大的。

我们知道了Memory Profiler中的数值是从上边的第一个表格的数值计算而来,那第一个表格的数值又是从何而来呢? 我们接着往下看!!

二 查看Smap数据

这里我们选取手机上的一个应用Phoneix查看一下其内存占用情况: 我这边实现了一个简单的脚本如下:

#! /bin/bash
process_name=$1
PID=$(adb shell pidof ${process_name})
adb shell run-as com.transsion.phoenix "cat /proc/${PID}/smaps > /data/local/tmp/smaps.txt"
adb pull /data/local/tmp/smaps.txt $2

执行 bash pull_smaps.sh com.transsion.phoneix 1.0.0_smaps.txt

我们可以简单看一下相关的smap文件:

image.png

看起来是不是一头雾水,这一个个的内存区间都是干什么用的?

我从github上copy来一个python3的脚本,专门做smap数据的解析,目前作者已经找不到的!

解析结果如下(解析结果较长,我将部分解析结果做了省略):

Unknown : 9.861 M
    pss: 8.443 M
    swapPss: 1.418 M
        [anon:partition_alloc] : 8520 kB
        [anon:.bss] : 935 kB
        [anon:linker_alloc] : 189 kB
        [anon:bionic_alloc_small_objects] : 120 kB
        [anon:thread signal stack] : 24 kB
        [anon:cfi shadow] : 24 kB
        [anon:System property context nodes] : 20 kB
        [anon:atexit handlers] : 9 kB
        [anon:arc4random data] : 8 kB
        [anon:bionic_alloc_lob] : 4 kB
Dalvik : 30.602 M
    pss: 29.222 M
    swapPss: 1.380 M
        [anon:dalvik-main space (region space)] : 23336 kB
        [anon:dalvik-free list large object space] : 4233 kB
        [anon:dalvik-zygote space] : 2525 kB
        [anon:dalvik-non moving space] : 508 kB
Native : 126.734 M
    pss: 99.627 M
    swapPss: 27.107 M
        [anon:scudo:primary] : 84810 kB
        [anon:scudo:secondary] : 41924 kB
Dalvik Other : 21.381 M
    pss: 18.341 M
    swapPss: 3.040 M
        [anon:dalvik-LinearAlloc] : 10612 kB
        /memfd:jit-cache (deleted) : 6344 kB
        [anon:dalvik-DEX data] : 3364 kB
                ...
                ...
Stack : 8.188 M
    pss: 6.148 M
    swapPss: 2.040 M
        [anon:stack_and_tls:4854] : 168 kB
        [anon:stack_and_tls:4870] : 148 kB
        [stack] : 148 kB
        [anon:stack_and_tls:4878] : 144 kB
        [anon:stack_and_tls:4877] : 144 kB
        ...
        ...
Cursor : 0.000 M
    pss: 0.000 M
    swapPss: 0.000 M
Ashmem : 0.204 M
    pss: 0.204 M
    swapPss: 0.000 M
        /dev/ashmem/gralloc_shared_memory (deleted) : 50 kB
        /dev/ashmem/MessageQueue (deleted) : 48 kB
        /dev/ashmem/AshmemAllocator_hidl (deleted) : 38 kB
        ...
        ...
Gfx dev : 0.000 M
    pss: 0.000 M
    swapPss: 0.000 M
Other dev : 0.020 M
    pss: 0.020 M
    swapPss: 0.000 M
        /dev/binderfs/binder : 12 kB
        /dev/binderfs/hwbinder : 8 kB
.so mmap : 9.051 M
    pss: 8.839 M
    swapPss: 0.212 M
        /vendor/lib64/egl/libGLES_mali.so : 4044 kB
        /system/lib64/libhwui.so : 661 kB
        /system/lib64/libstagefright.so : 328 kB
        ...
        ...
.jar mmap : 2.217 M
    pss: 2.217 M
    swapPss: 0.000 M
        /system/framework/framework.jar : 1241 kB
        /data/data/com.transsion.phoenix/app_pccache/5/3ADD07A77E5BC23D41D5235C3F0C964B75D847A9/pcam.jar : 360 kB
        /apex/com.android.art/javalib/core-icu4j.jar : 265 kB
        /apex/com.android.art/javalib/core-oj.jar : 173 kB
        /apex/com.android.art/javalib/bouncycastle.jar : 78 kB
        ...
        ...
.apk mmap : 17.179 M
    pss: 16.923 M
    swapPss: 0.256 M
        /data/app/~~l7NnUJ5BUuoASfLO4ps6rQ==/com.google.android.webview-cmrXpqowpCjrrfYc0lesEQ==/base.apk : 14642 kB
        /data/user_de/0/com.google.android.gms/app_chimera/m/0000004d/dl-AdsFdrDynamite.integ_221310604100000.apk : 850 kB
        /data/app/~~WkMxdLO_EFZvRB7Y7O4Tfw==/com.transsion.phoenix-PVevdVSKlTJCD3Q2aDbscQ==/base.apk : 783 kB
        ...
        ...
.ttf mmap : 0.140 M
    pss: 0.140 M
    swapPss: 0.000 M
        /system/fonts/Roboto-Bold.ttf : 99 kB
        /system/fonts/Roboto-Regular.ttf : 37 kB
        /system/fonts/NotoSansLaoUI-Regular.ttf : 4 kB
.dex mmap : 44.585 M
    pss: 19.645 M
    swapPss: 24.940 M
        [anon:dalvik-classes7.dex extracted in memory from /data/app/~~WkMxdLO_EFZvRB7Y7O4Tfw==/com.transsion.phoenix-PVevdVSKlTJCD3Q2aDbscQ==/base.apk!classes7.dex] : 10932 kB
        [anon:dalvik-classes6.dex extracted in memory from /data/app/~~WkMxdLO_EFZvRB7Y7O4Tfw==/com.transsion.phoenix-PVevdVSKlTJCD3Q2aDbscQ==/base.apk!classes6.dex] : 10864 kB
        [anon:dalvik-classes.dex extracted in memory from /data/app/~~WkMxdLO_EFZvRB7Y7O4Tfw==/com.transsion.phoenix-PVevdVSKlTJCD3Q2aDbscQ==/base.apk] : 9328 kB
        ...
        ...
.oat mmap : 0.079 M
    pss: 0.079 M
    swapPss: 0.000 M
        /system/framework/arm64/boot-framework.oat : 36 kB
        /apex/com.android.art/javalib/arm64/boot.oat : 19 kB
        /apex/com.android.art/javalib/arm64/boot-core-icu4j.oat : 13 kB
        ...
        ...
.art mmap : 11.323 M
    pss: 8.984 M
    swapPss: 2.339 M
        [anon:dalvik-/system/framework/boot-framework.art] : 7112 kB
        [anon:dalvik-/apex/com.android.art/javalib/boot.art] : 1711 kB
        [anon:dalvik-/system/framework/boot-telephony-common.art] : 756 kB
        ...
        ...
Other mmap : 2.297 M
    pss: 2.297 M
    swapPss: 0.000 M
        /system/fonts/NotoSansCJK-Regular.ttc : 1030 kB
        /data/misc/shared_relro/libwebviewchromium64.relro : 626 kB
        /data/data/com.transsion.phoenix/app_webview/BrowserMetrics/BrowserMetrics-6281F38F-12BF.pma : 256 kB
        ...
        ...

以上合并的计算结果就是第二部分中第一个表的数据,其中有略微的差异, 是因为两个数据dump的时机是有微小的时间间隔导致。另外此部分的合并数据,没有包含GL相关的数据,GL相关的数据需要从显存中读取,此处暂时不做进一步的探讨。

可以清晰的看到,使用smaps统计出来的内存和使用adb shell dumpsys meminfo是一致的,但是smaps聚合统计到的数据,可以清晰的看到哪一个so、ttf、oat所占的内存,这部分信息adb shell dumpsys meminfo是不具有的。

从合并以后的结果来看,我们知道了进程每个部分详细的内存占用情况:

  • 如果是Native部分内存占用的比较高,如果是Android 8.0以上,我们首先去分析Bimap占用的内存是否异常。如果Bitmap占用正常, 那么此部分就主要是通过malloc和mmap进行分配的,我们可以通过Loliprofile或者Malloc Debug进行进一步的堆栈抓取,来解决不合理的内存分配。也可以通过xHook或者字节跳动的Native Hook工具去hook内存分配函数做进一步的内存定位;

  • 如果Code部分占用过多, 我们可以考虑优化包大小或者不合理的字体载入等;

  • 如果Java Heap占用比较多,如果是Android 8.0 以下的设备,可以去看一下Bitmap的占用,如果Bitmap占用是正常的,需要分析是否有不合理的Java引用或者内存泄漏,此部分可以借助Android Studio自带的内存工具或者MAT做进一步分析, 此部分相对比较简单。

三 smaps文件

Linux在2.6版本之后有一个proc伪文件,在它下面记录各种信息,其中在proc/pid/smaps记录某个pid的内,smaps记录内存详细的使用情况。使用下面的命令可以读文件的值

cat /proc/pid/smaps

文件格式如下:

image.png

文件字段意义:

  • 400ca000-400cb000:本段虚拟内存的地址范围
  • r-xp :文件权限,r(读)、w(写)、x(执行)、p表示私有,s代表共享,如果不具有哪项权限用"-"代替
  • 00000000 :映射文件的偏移量
  • b3:11 :文件设备号
  • 1345 :被映射到虚拟内存文件的映索节点
  • /system/lib/libplddbgutil.so:文件名称
  • Size:相应虚拟地址空间的大小
  • RSS: 正在使用的物理内存的大小
  • Shared_Clean: Rss中和其他进程共享的未使用页数
  • Shared_Dirty: Rss和其他进程共享已经使用的页数
  • Private_Clean: Rss私有区域未使用的页数
  • Private_Dirty: Rss私有区域已经使用的页数

smaps文件一般有啥作用呢,譬如我们通过dumpsys meminfo 获取内存时,发现某一项内存数据异常,想弄清楚数据都是有哪些文件产生,我们就可以通过读取smaps详细排查。

原文
https://juejin.cn/post/7098341701999132686
https://juejin.cn/post/6844903540192706573

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容