内存拆解分析表:学习版[图片]

对拆解system中主要是对比测试机和对比机之间的差距,测试机那些地方高于对比机

拆解表,作为理解

在拆解表中system测试机比对比机多出113M

这说明是有问题的

对system拆解:

system12345
对比机9102294380941069391081628
测试机102520103318103183103160103458
差值1149889389077925021830
Java Heap12345
对比机13208415325617262010668092732
测试机15481618234412139214783298896
差值2273229088-51228411526164
Java Heap12345
对比机5813655360556925534854488
测试机4256842292443404410444464
差值-15568-13068-11352-11244-10024
Code12345
对比机6392864872640526439262700
测试机164736165088155324155492155540
差值100808100216912729110092840
Stack12345
对比机43484092409240923984
测试机34963500350435083508
差值-852-592-588-584-476
Graphics12345
对比机1662016620166201662016620
测试机19921992199219921992
差值-14628-14628-14628-14628-14628
Private Other12345
对比机1376813604136081362413772
测试机4192841936419524194441616
差值2816028332283442832027844

上面拆解表是模块2的集合版本

从上面表我们可以看出来内存主要出现最大差异的是Code

我们可以根据此表进程计算拆解,模块2主要是模块1的总体进程

所以我们要知道模块2中的Code,那些在模块1中

Java Heap = Dalvik Heap private dirty+ .art mmap private (clean+ dirty) = 27412 + 4 + 7824 = 35240Native Heap = Native private dirty = 98156Code = .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) = 23440Stack = Stack private dirty = 6144 Graphics = GL mtrack private (clean + dirty) + EGL mtrack private(clean + dirty) = (0 + 0) + (28769 + 0) = 28769Private other= TOTAL private (clean + dirty) - Java Heap - Native Heap- Code- Stack -Graphic = (205245 + 7780) - 35240 - 98156 - 23440 - 6144 - 28769 = 21276System = TOTAL - TOTAL private (clean + dirty)  = 309121 - (205245 + 7780) = 96096

这个公式只是计算出各个模块2中的各项指标,只是案例不要和自己项目中进行对比,但是公式是一样的,没有什么区别

详细教学:

https://www.jianshu.com/p/af22eb653fc3

内存拆解分析通用文档

内存分析通用文档

说明

该文档对常见的内存超标问题进行总结,并给出内存拆解方式

一. 初略分析

查看process整体内存状态的两种方式:

dumpsys meminfo <pid>

根据测试提供的dumpsys meminfo数据,可先初略看一下内存状态,如下图

  tab1是较为详细的内存分布,一般详细的拆解需要从tab1中查看;

  tab4为Objects是统计App内部组件对象个数,其中Views、ViewRootImpl以及Activities个数,若这些项占用过多,考虑出现了内存泄露或是测试出现异常;

  tab2则是对tab1的再统计,其它字段较易理解,Graphics和System的统计逻辑如下已给出。

tab1中每项的内存占用

对内存进行的一般依据tab1的数据,每一个小项的内存值为 Pss Total + SwapPss,如上图中,每一项对应的内存值如下:

Name(每一块的内存分布)

Pss Total

SwapPss Dirty

总占用内存

Native Heap

18861

17706

36567

Dalvik Heap

6016

51

6067

Stack

924

660

......

......

......

......

EGL mtrack

44560

0

44560

GL mtrack

5840

0

5840

(EGL多为应用在前台所占用的内存)

System内存

System = Total SwapPss + 共享内存;

(注:System=Total Pss - Total Private Clean - Total Private Dirty,而Total Pss = 各个部分的PSS值 + SwapPSS Dirty,顾可以将System转换成上式;)

(注:SwapPss 表示相关内存回收至Swap,该内存并未释放,如图中数据:

这里Native Heap 中的Pss total为18861并未包含SwapPss 11706,可以认为Native Heap实际总占用内存为 Pss Total + SwapPss Dirty,这里SwapPss占用过多的依然需要对Native Heap进行拆解 )

Graphics内存

Graphics = EGL mtrack + GL mtrack + Gfx dev(该块Mtk机型没有)

一般情况下EGL和GL占用的内存会比较多,需要对这两块进行分析和拆解,拆解方式在下文详解

  1. Profiler查看内存

Android Studio会自带Profiler,可以使用手机对问题进行本地复现,或是导入一个 HPROF (.hprof) 文件,大自查看当时的内存状态,详细可查看官方文档:

https://developer.android.com/studio/profile/memory-profiler?hl=zh-cn#capture-heap-dump

(可以利用该工具大致查看内存占用和对象的持有关系,可结合mat工具进行查看)

二. 常见内存拆解

1.Native Heap内存拆解

通用查看native heap部分内存可用以下方式,通常会需要与对比机进行对比,以确认多出的调用内存

http://t.csdnimg.cn/XJjAX

http://t.csdnimg.cn/NqKqS

2.Graphics内存拆解

一般情况下占用过大的是EGL和GL部分的内存

MTK平台

  • EGL

一般情况下应用在后台时,EGL内存值为0,dumpsys meminfo <pid>中不会包含EGL mtrack,如

在kernel-5.10之前(查看kernel版本方法 adb shell cat /proc/version),EGL统计的是ION memory,可以通过节点 /proc/ion/ion_mm_heap 大自看出ION内存的出处,如下:

//显示了各个进程和ion占用大小,会列出的各别进程使用的ion内存,但有重复包含share部分client(        dbg_name)              pid size(cnt)--size(cnt)          address        threshold
----------------------------------------------------
time 1 9611282 msndroid.settings(         gralloc)            26696         56262656(8)--56262656(8) 0x0000000027997a3d 1073741824iui.miwallpaper(         gralloc)             1897         20373504(2)--20373504(2) 0x000000003957d3fd 1073741824ndroid.systemui(         gralloc)             2101          9945088(21)--9945088(21) 0x000000002729ee06 1073741824system_server(         gralloc)             1436         14745600(4)--14745600(4) 0x00000000ebaab696 1073741824surfaceflinger(         gralloc)              742        153645056(38)--153645056(38) 0x000000003d0e9fb1 1073741824com.miui.home(         gralloc)             2103         57532416(14)--57532416(14) 0x0000000079511560 1073741824composer@2.1-se(         gralloc)              681        109453312(26)--109453312(26) 0x00000000bc190eb0 1073741824display(     from_kernel)                1         35995648(8)--35995648(8) 0x000000007adc8128 1073741824disp_decouple(     from_kernel)              256          7581696(1)--7581696(1) 0x0000000072ff9cec 1073741824......
//显示了应用的每个buffer
client(0x00000000ebaab696) system_server (gralloc) pid(1436) ================>handle=0x000000003fe10505 (id: 3), buffer=0x000000003f3049f3, heap=10, fd= 738, ts: 9606350ms (1)handle=0x00000000fbf85599 (id: 4), buffer=0x00000000c4044948, heap=10, fd= 740, ts: 9606570ms (2)handle=0x000000003140ccc8 (id: 1), buffer=0x0000000029dc229f, heap=10, fd= 726, ts: 9564768ms (3)handle=0x000000001fb4bd05 (id: 2), buffer=0x000000000d7fabfd, heap=10, fd= 734, ts: 9564797ms (4)

前台EGL mtrack出现占用过大的情况时,可先看一下该节点中的buffer占用,以及buffer释放时间(有些buffer若释放不及时会出现累加,EGL就会出现峰值,一般在动画或是图片多的场景下会出现峰值)

在kernel-5.10之后的EGL内存统计的是dmabuf,统计逻辑出现修改,且存在单个进程内存统计不准确的情况,后续补充分析方法。

先摆出结论:在VMWare虚拟机平台上,宿主机可以通过一个叫Balloon driver(vmware_balloon module)的驱动程序模拟客户机linux内部的进程占用内存,被占用的内存实际上是被宿主机调度到其他客户机去了。 但这种驱动程序模拟的客户机进程在linux上的内存动态分配并没有被linux内核统计进来,于是造成了上述问题的现象。

三、问题描述

通过free -htop查看内存消耗,发现used已接近最大可用内存,但各进程常驻内存(RES)远比used要小。

  • GL:该部分内存的统计可查看 /sys/kernel/debug/mali0/ctx/<PID_X>/mem_profile节点,如

  • //2101_5中的 5 需要进入/sys/kernel/debug/mali0/ctx/目录后才能看到
    lancelot:/ # cat /sys/kernel/debug/mali0/ctx/2101_5/mem_profile
    Channel: Default Heap (Total memory: 5166136)13:                    9 / 3835214:                    1 / 1248018:                   12 / 236188819:                    8 / 209715220:                    1 / 656264Channel: Texture (Total memory: 45954240)14:                    9 / 10092815:                    7 / 15782416:                    1 / 3328018:                    1 / 20889619:                    5 / 162611220:                    2 / 136806421:                    2 / 346521622:                    3 / 798720023:                    2 / 938803224:                    2 / 21618688
    ......

    该节点中的每个字段的意思如下:

  • 一般只需知道Total memory后的值为每类buffer占用的总值,该节点信息可排相关内存。

    高通平台

  • EGL和Gl通常都可使用/d/kgsl/proc/<pid>/mem查看底层内存块,如:

  •  gpuaddr         useraddr             size    id      flags       type            usage sglen     mapcnt eglsrf eglimg      inode
    0000000000000000 0000000000000000           196608     1  --w---N--     gpumem           any(0)     0          0      0      0          0
    0000000000000000 0000000000000000            16384     2  --w---Y--     gpumem          command     0          1      0      0          0
    0000000000000000 0000000000000000             4096     3  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000             4096     4  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000             4096     5  --w---Y--     gpumem               gl     0          1      0      0          0
    0000000000000000 0000000000000000             4096     6  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000             4096     7  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000            20480     8  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000             4096     9  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000             4096    10  --w---Y--     gpumem           any(0)     0          1      0      0          0
    0000000000000000 0000000000000000           196608    11  --w---N--     gpumem           any(0)     0          0      0      0          0
    ...
    0000000000000000 0000000000000000         10444800    71  --wLb-N--        ion        egl_image   165          0      0      1     531162

    EGL过大时,可查看type为ion的内存块是否占用过多

    GL和Gfx过大时,可查看type为gpumem的内存块是否占用过多

    3. System内存拆解

    System

    其为 共享内存 + SwapPss

    从dumpsys meminfo中可以看到Total SwapPss的值,例如

  •              Pss  Private  Private  SwapPss      Rss     Heap     Heap     HeapTotal    Dirty    Clean    Dirty    Total     Size    Alloc     Free------   ------   ------   ------   ------   ------   ------   ------Native Heap     9403     9104      272    12602     9824    23796    22437     1358Dalvik Heap    10529    10468        0     1215    11000    14810     7405     7405Dalvik Other     3572     3068        4      207     4564                           Stack      772      772        0      404      772                           Ashmem       26        0        0        0      552                           Other dev       35        0       28        0      428                           .so mmap     3188      220       28      211    31964                           .jar mmap     2698        0      180        0    28208                           .apk mmap     6683        0     1204        0    14764                           .ttf mmap       65        0        0        0      236                           .dex mmap     6963        0     6940        4     7464                           .oat mmap      877        0       12        0    13588                           .art mmap     2639     1924       24      444    10444                           Other mmap     1640       36      888        0     5036                           EGL mtrack    10323    10323        0        0    10323                           GL mtrack    14214    14214        0        0    14214                           Unknown      133      128        4      410      188                           TOTAL    89257    50257     9584    15497   163569    38606    29842     8763App SummaryPss(KB)                        Rss(KB)------                         ------Java Heap:    12416                          21444Native Heap:     9104                           9824Code:     8584                          97220Stack:      772                            772Graphics:    24537                          24537Private Other:     4428System:    29416Unknown:                                    9772TOTAL PSS:    89257            TOTAL RSS:   163569       TOTAL SWAP PSS:    15497        

    从dumpsys meminfo的数据中可以看到,Total SwapPss为15497,而Native Heap中的SwapPss为12602,这块内存只是系统将暂时未用到的内存回收至swap区,依然需要从Native Heap入手进行拆解。

    共享内存

    是对某块内存进行共享使用,现未发现有较大占用的情况,若需要计算每块共享内存,可使用每一项的Pss Total - Private Dirty - Private Clean ,再寻找某一项的问题,如上述数据中 .apk mmap共享内存为6683 - 0 - 1204 = 5479

    4. smaps详解

    读取/proc/pid/smaps节点的信息,如smaps,下表为smaps的注解,除了EGL和GL的内存,其他内存都会反应在smaps中,如:

  •  701b3000-701b4000             r--p           00003000                                          
    //虚拟内存段的开始和结束位置        内存段的权限     该虚拟内存段起始地址在对应的映射文件中以页为单位的偏移量fd:13                       51                  /apex/com.android.art/javalib/arm64/boot-apache-xml.oat                      
    //文件的主设备号和次设备号   被映射到虚拟内存的文件的索引节点号     被映射到虚拟内存的文件名称Size:                  4 kB  //虚拟内存空间大小
    KernelPageSize:        4 kB  //内核一页的大小
    MMUPageSize:           4 kB  //MMU页大小
    //实际分配的内存 Rss=Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty
    Rss:                   0 kB  
    //是平摊计算后的实际物理使用内存 Pss=private_clean+private_dirty+按比例均分的shared_clean、shared_dirty。
    Pss:                   0 kB Shared_Clean:          0 kB
    Shared_Dirty:          0 kB
    Private_Clean:         0 kB
    Private_Dirty:         0 kBReferenced:            0 kB  //当前页面被标记为已引用或者包含匿名映射
    Anonymous:             0 kB  //匿名映射的物理内存
    LazyFree:              0 kB
    AnonHugePages:         0 kB
    ShmemPmdMapped:        0 kB  //PMD页面已经被映射的共享(shmem / tmpfs)内存量
    FilePmdMapped:        0 kB  //由hugetlbfs页面支持的内存使用量
    Shared_Hugetlb:        0 kB
    Private_Hugetlb:       0 kB
    Swap:                  0 kB  //存在于交换分区的数据大小
    SwapPss:               0 kB  //逻辑就跟pss一样,针对的是Swap的内存
    Locked:                0 kB
    THPeligible:                0  //映射是否符合分配THP的条件
    VmFlags: rd mr mw me   //表示与特定虚拟内存区域关联的内核标志

    脚本:

    check_smaps.py

    使用方法:

  • #!/usr/bin/env python3from collections import namedtuple
    from sys import argv, stdin
    import refd = stdin if len(argv) != 2 else open(argv[1], 'r')# Reference: 
    #   file: kernel/msm-4.9/fs/proc/task_mmu.c
    #   func: show_map_vmaRE_MAPPING_LINE = re.compile('(?P<begin_addr>[a-f0-9]+)-(?P<end_addr>[a-f0-9]+)'' (?P<permission>....) (?P<pgoff>[a-f0-9]+)'' (?P<major_dev>[a-f0-9]+):(?P<minor_dev>[a-f0-9]+)'' (?P<ino>\d+)( *(?P<name>.*))?'
    )PSS_LINE = "^Pss: *([0-9]+) kB"Entry = namedtuple('Entry', ['name', 'num', 'max', 'sum'])
    class Entry:def __init__(self, name, num, max, sum):self.name = nameself.num = numself.max = maxself.sum = sumdef __lt__(self, other):return self.sum > other.sumstats = dict()for line in fd:line = line.rstrip()r = RE_MAPPING_LINE.match(line)if r:name = r.groupdict()['name']if not name:name = '(NONAME)'flag = 1continueif flag:pss = re.match(PSS_LINE, line)if pss:flag = 0size = int(pss.group(1))if size: if name in stats:stats[name].num += 1stats[name].max = max(size, stats[name].max)stats[name].sum += sizeprint("name:{}, num:{}, max:{}, sum:{}".format(name, stats[name].num, stats[name].max, stats[name].sum))else:entry = Entry(name, 1, size, size)stats[name] = entryprint("name:{}, num:{}, max:{}, sum:{}".format(name, stats[name].num, stats[name].max, stats[name].sum))
    ''' if not r:print('Invalid lines "{}"'.format(line))continuesize = int(r.groupdict()['end_addr'], 16) - int(r.groupdict()['begin_addr'], 16)name = r.groupdict()['name']if not name:name = '(NONAME)'if name.startswith('[stack'):name = '[stack:*]'if name in stats:stats[name].num += 1stats[name].max = max(size, stats[name].max)stats[name].sum += sizeelse:entry = Entry(name, 1, size, size)stats[name] = entrymmap = dict()
    for f in sorted([_[1] for _ in stats.items()]):out = f.name.split(".")[-1]if(out == "so"):mmap[name]=".so mmap"if f.name in mmap:mmap[name].name = f.namemmap[name].num += 1mmap[name].max = max(f.size, mmap[name].max)mmap[name].sum += sizeelse:entry = Entry(name, 1, size, size)mmap[name] = entryelif (out == "jar"):elif (out == ".odex" or out == ".dex" or out == ".vdex"):else:continue
    '''
    format_f = '{:<120} {:<12} {:<12} {:<12}'.format
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    print('-' * 79)
    for foo in sorted([_[1] for _ in stats.items()]):print(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print('-' * 79)
    print(format_f('Total', sum([_[1].num for _ in stats.items()]),max([_[1].max for _ in stats.items()]),sum([_[1].sum for _ in stats.items()]),
    ))print("{}{}{}".format('-' * 40, "dev", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_dev = 0
    for foo in sorted([_[1] for _ in stats.items()]):if len(foo.name.split("/")) >= 2:out = foo.name.split("/")[1]if (out == "dev"):sum_dev = sum_dev + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_dev))print("{}{}{}".format('-' * 40, "so", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_so = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "so"):sum_so = sum_so + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_so))print("{}{}{}".format('-' * 40, "jar", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_jar = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "jar"):sum_jar = sum_jar + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_jar))print("{}{}{}".format('-' * 40, "apk", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_apk = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "apk"):sum_apk = sum_apk + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_apk))print("{}{}{}".format('-' * 40, "ttf", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_ttf = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "ttf"):sum_ttf = sum_ttf + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_ttf))print("{}{}{}".format('-' * 40, "dex", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_dex = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "odex" or out == "dex" or out == "vdex"):sum_dex = sum_dex + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_dex))print("{}{}{}".format('-' * 40, "oat", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_oat = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "oat"):sum_oat = sum_oat + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_oat))print("{}{}{}".format('-' * 40, "art", '-' * 40))
    print(format_f('Name', 'Num(count)', 'Max(KB)', 'Sum(KB)'))
    sum_oat = 0
    for foo in sorted([_[1] for _ in stats.items()]):out = foo.name.split(".")[-1]if (out == "art" or out=="art]"):sum_oat = sum_oat + foo.sumprint(format_f(foo.name[:120], foo.num, foo.max, foo.sum))
    print(format_f('Sum', '', '', sum_oat))

    Adb pull /proc/<pid>/smaps .

    python check_smaps.py smaps

    可筛选smaps中的信息,smaps中只能显示出哪一块内存比较大,其他细项依然需要从其他角度定位。

  • 学习拆解MEMINFO

    一、背景

    近期在公司的某台linux虚拟机上,发现内存几乎消耗殆尽,但找不到其去向。 在调查过程中,重点分析了/proc/meminfo文件,对其内存占用进行了学习与分析。

  • 二、环境

  • 虚拟机OS : CentOS Linux release 7.4.1708 (Core)

  • 虚拟机平台 : VMWare

  • 三、问题描述

    通过free -htop查看内存消耗,发现used已接近最大可用内存,但各进程常驻内存(RES)远比used要小。

    先摆出结论:在VMWare虚拟机平台上,宿主机可以通过一个叫Balloon driver(vmware_balloon module)的驱动程序模拟客户机linux内部的进程占用内存,被占用的内存实际上是被宿主机调度到其他客户机去了。 但这种驱动程序模拟的客户机进程在linux上的内存动态分配并没有被linux内核统计进来,于是造成了上述问题的现象。

3.1 top 结果

按内存消耗排序,取消耗大于0的部分

top - 16:46:45 up 8 days, 10:25,  1 user,  load average: 0.00, 0.01, 0.05
Tasks: 109 total,   1 running, 108 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.0 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7994080 total,   185776 free,  7625996 used,   182308 buff/cache
KiB Swap:  4157436 total,   294944 free,  3862492 used.   115964 avail Mem PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND               DATA3725 root      20   0 9057140 1.734g   5020 S   0.3 22.7 367:48.86 java               88826801087 mysql     20   0 2672240 233064   1076 S   0.0  2.9 102:33.71 mysqld             2596840496 root      20   0   36828   3512   3388 S   0.0  0.0   0:31.13 systemd-journal        35614564 root      20   0  145700   2424   1148 S   0.0  0.0   0:02.94 sshd                   9241 root      20   0  128164   2404    724 S   0.0  0.0   1:02.18 systemd              8462814713 root      20   0  157716   2204   1512 R   0.0  0.0   0:00.08 top                   117614568 root      20   0  115524   1784   1272 S   0.0  0.0   0:00.59 bash                   632687 root      20   0  305408   1548   1168 S   0.0  0.0  13:59.34 vmtoolsd             75352676 root      20   0  216388   1240    872 S   0.0  0.0   1:56.69 rsyslogd            148768682 root      20   0  472296    908    160 S   0.0  0.0   1:06.73 NetworkManager      222852684 root      20   0   24336    752    444 S   0.0  0.0   0:22.19 systemd-logind         504690 polkitd   20   0  534132    560    220 S   0.0  0.0   0:07.34 polkitd             450080677 dbus      20   0   32772    460    128 S   0.0  0.0   0:08.34 dbus-daemon           8900688 root      20   0   21620    452    296 S   0.0  0.0   4:42.68 irqbalance             488698 root      20   0  126232    432    328 S   0.0  0.0   0:30.25 crond                 1312922 root      20   0  562392    412     28 S   0.0  0.0   4:52.69 tuned               304472924 root      20   0  105996    188     92 S   0.0  0.0   0:03.64 sshd                   760653 root      16  -4   55452     84      0 S   0.0  0.0   0:08.81 auditd                8664532 root      20   0   46684      4      4 S   0.0  0.0   0:02.81 systemd-udevd         1916705 root      20   0  110044      4      4 S   0.0  0.0   0:00.02 agetty                 344

3.2 top结果第四行内存总体使用情况

属性大小(G)说明
total7.6可分配内存总计值
used7.26已分配内存
free0.17未分配内存
buff/cache0.17buff与缓存
  • 各属性满足公式:total = used + free + buff/cache

  • used在该linux版本(centos7)上,已经反映实际分配的内存,不需要再去除buff/cache部分

3.3 top进程列表内存相关列统计

合计(G)说明
VIRT14.236进程申请的虚拟内存大小,申请不意味着分配,该值与实际内存消耗关系不大。
RES1.9747进程常驻内存,包含进程间共享内存。
SHR0.0171进程间共享内存,该值是推算出来的,存在误差,意义不大。

3.4 问题来了

RES合计值比used少了5G多!这些内存哪去了?

理论上,各进程的RES合计值因为会重复计算共享内存,应该比used值略大。实际上这两个值也往往是接近的,不应该差这么多。

四、清查linux内存消耗

为了进一步检查linux中内存消耗的去向,需要对/proc/meminfo文件进行一次彻底的分析统计。

linux上各种内存查看工具如free,top实际上都是从/proc下面找linux内核的各种统计文件。

4.1 /proc/meminfo内容

MemTotal:        7994080 kB
MemFree:          125256 kB
MemAvailable:     932412 kB
Buffers:               8 kB
Cached:           993796 kB
SwapCached:          252 kB
Active:          1182220 kB
Inactive:        1213960 kB
Active(anon):     693796 kB
Inactive(anon):   717156 kB
Active(file):     488424 kB
Inactive(file):   496804 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       4157436 kB
SwapFree:        4157172 kB
Dirty:                 8 kB
Writeback:             0 kB
AnonPages:       1402140 kB
Mapped:            41584 kB
Shmem:              8576 kB
Slab:             143220 kB
SReclaimable:      86720 kB
SUnreclaim:        56500 kB
KernelStack:        5360 kB
PageTables:         7184 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     8154476 kB
Committed_AS:    2073776 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      191584 kB
VmallocChunk:   34359310332 kB
HardwareCorrupted:     0 kB
AnonHugePages:   1284096 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       89920 kB
DirectMap2M:     4104192 kB
DirectMap1G:     6291456 kB

4.2 meminfo内容分析

属性大小(k)说明扩展说明
MemTotal:7994080可供linux内核分配的内存总量。比物理内存总量少一点,因为主板/固件会保留一部分内存、linux内核自己也会占用一部分内存。
MemFree:125256表示系统尚未分配的内存。
MemAvailable:932412当前可用内存。MemFree只是尚未分配的内存,并不是所有可用的内存。有些已经分配掉的内存是可以回收再分配的。比如cache/buffer、slab都有一部分是可以回收的,这部分可回收的内存加上MemFree才是系统可用的内存,即MemAvailable。同时要注意,MemAvailable是内核使用特定的算法估算出来的,并不精确。
Buffers:8块设备(block device)所占用的特殊file-backed pages,包括:直接读写块设备,以及文件系统元数据(metadata)比如superblock使用的缓存页。Buffers内存页同时也在LRU list中,被统计在Active(file)或Inactive(file)之中。
Cached:993796所有file-backed pages用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页),比如进程的代码、映射的文件都是file-backed,而进程的堆、栈都是不与文件相对应的、就属于匿名页。file-backed pages在内存不足的时候可以直接写回对应的硬盘文件里,称为page-out,不需要用到交换区(swap);而anonymous pages在内存不足时就只能写到硬盘上的交换区(swap)里,称为swap-out。
SwapCached:252SwapCached包含的是被确定要swap-out,但是尚未写入交换区的匿名内存页。SwapCached内存页会同时被统计在LRU或AnonPages或Shmem中,它本身并不占用额外的内存。
Active:1182220active包含active anon和active fileLRU是一种内存页回收算法,Least Recently Used,最近最少使用。LRU认为,在最近时间段内被访问的数据在以后被再次访问的概率,要高于最近一直没被访问的页面。于是近期未被访问到的页面就成为了页面回收的第一选择。Linux kernel会记录每个页面的近期访问次数,然后设计了两种LRU list: active list 和 inactive list, 刚访问过的页面放进active list,长时间未访问过的页面放进inactive list,回收内存页时,直接找inactive list即可。另外,内核线程kswapd会周期性地把active list中符合条件的页面移到inactive list中。
Inactive:1213960inactive包含inactive anon和inactive file
Active(anon):693796活跃匿名页,anonymous pages(匿名页)。
Inactive(anon):717156非活跃匿名页
Active(file):488424活跃文件内存页
Inactive(file):496804非活跃文件内存页
Unevictable:0因为种种原因无法回收(page-out)或者交换到swap(swap-out)的内存页Unevictable LRU list上是不能pageout/swapout的内存页,包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(同时被统计在Mlocked中)、和ramfs。在unevictable list出现之前,这些内存页都在Active/Inactive lists上,vmscan每次都要扫过它们,但是又不能把它们pageout/swapout,这在大内存的系统上会严重影响性能,unevictable list的初衷就是避免这种情况的发生。
Mlocked:0被系统调用"mlock()"锁定到内存中的页面。Mlocked页面是不可收回的。被锁定的内存因为不能pageout/swapout,会从Active/Inactive LRU list移到Unevictable LRU list上。Mlocked与以下统计项重叠:LRU Unevictable,AnonPages,Shmem,Mapped等。
SwapTotal:4157436swap空间总计
SwapFree:4157172当前剩余swap
Dirty:8需要写入磁盘的内存页的大小Dirty并不包括系统中全部的dirty pages,需要再加上另外两项:NFS_Unstable 和 Writeback,NFS_Unstable是发给NFS server但尚未写入硬盘的缓存页,Writeback是正准备回写硬盘的缓存页。
Writeback:0正在被写回的内存页的大小
AnonPages:1402140Anonymous pages(匿名页)数量 + AnonHugePages(透明大页)数量进程所占的内存页分为anonymous pages和file-backed pages,理论上,所有进程的PSS之和 = Mapped + AnonPages。PSS是Proportional Set Size,每个进程实际使用的物理内存(比例分配共享库占用的内存),可以在/proc/[1-9]*/smaps中查看。
Mapped:41584正被用户进程关联的file-backed pagesCached包含了所有file-backed pages,其中有些文件当前不在使用,但Cached仍然可能保留着它们的file-backed pages;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为mapped。
Shmem:8576Shmem统计的内容包括:1.shared memory;2.tmpfs和devtmpfs。所有tmpfs类型的文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件在没有关闭的情况下被删除,空间仍然不会释放,shmem不会减小,可以用 lsof -a +L1 /<mount_point> 命令列出这样的文件。shared memory被视为基于tmpfs文件系统的内存页,既然基于文件系统,就不算匿名页,所以不被计入/proc/meminfo中的AnonPages,而是被统计进了:Cached或Mapped(当shmem被attached时候)。然而它们背后并不存在真正的硬盘文件,一旦内存不足的时候,它们是需要交换区才能swap-out的,所以在LRU lists里,它们被放在Inactive(anon) 或 Active(anon)或 unevictable (如果被locked的话)里。注意:/proc/meminfo中的 Shmem 统计的是已经分配的大小,而不是创建时申请的大小。
Slab:143220通过slab分配的内存,Slab=SReclaimable+SUnreclaimslab是linux内核的一种内存分配器。linux内核的动态内存分配有以下几种方式:1.alloc_pages/__get_free_page:以页为单位分配。2.vmalloc:以字节为单位分配虚拟地址连续的内存块。3.slab:对小对象进行分配,不用为每个小对象分配一个页,节省了空间;内核中一些小对象创建析构很频繁,Slab对这些小对象做缓存,可以重复利用一些相同的对象,减少内存分配次数。4.kmalloc:以slab为基础,以字节为单位分配物理地址连续的内存块。
SReclaimable:86720slab中可回收的部分。
SUnreclaim:56500slab中不可回收的部分。
KernelStack:5360给用户线程分配的内核栈消耗的内存页每一个用户线程都会分配一个kernel stack(内核栈),内核栈虽然属于线程,但用户态的代码不能访问,只有通过系统调用(syscall)、自陷(trap)或异常(exception)进入内核态的时候才会用到,也就是说内核栈是给kernel code使用的。在x86系统上Linux的内核栈大小是固定的8K或16K。Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里。RSS是Resident Set Size 实际使用物理内存(包含共享库占用的内存),可以在/proc/[1-9]*/smaps中查看。
PageTables:7184Page Table的消耗的内存页Page Table的用途是翻译虚拟地址和物理地址,它是会动态变化的,要从MemTotal中消耗内存。
NFS_Unstable:0发给NFS server但尚未写入硬盘的缓存页
Bounce:0bounce buffering消耗的内存页有些老设备只能访问低端内存,比如16M以下的内存,当应用程序发出一个I/O 请求,DMA的目的地址却是高端内存时(比如在16M以上),内核将在低端内存中分配一个临时buffer作为跳转,把位于高端内存的缓存数据复制到此处。
WritebackTmp:0正准备回写硬盘的缓存页
CommitLimit:8154476overcommit阈值,CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + SwapLinux是允许memory overcommit的,即承诺给进程的内存大小超过了实际可用的内存。commit(或overcommit)针对的是内存申请,内存申请不等于内存分配,内存只在实际用到的时候才分配。但可以申请的内存有个上限阈值,即CommitLimit,超出以后就不能再申请了。
Committed_AS:2073776所有进程已经申请的内存总大小
VmallocTotal:34359738367可分配的虚拟内存总计
VmallocUsed:191584已通过vmalloc分配的内存,不止包括了分配的物理内存,还统计了VM_IOREMAP、VM_MAP等操作的值VM_IOREMAP是把IO地址映射到内核空间、并未消耗物理内存
VmallocChunk:34359310332通过vmalloc可分配的虚拟地址连续的最大内存
HardwareCorrupted:0因为内存的硬件故障而删除的内存页
AnonHugePages:1284096AnonHugePages统计的是Transparent HugePages (THP),THP与Hugepages不是一回事,区别很大。Hugepages在/proc/meminfo中是被独立统计的,与其它统计项不重叠,既不计入进程的RSS/PSS中,又不计入LRU Active/Inactive,也不会计入cache/buffer。如果进程使用了Hugepages,它的RSS/PSS不会增加。而AnonHugePages完全不同,它与/proc/meminfo的其他统计项有重叠,首先它被包含在AnonPages之中,而且在/proc/<pid>/smaps中也有单个进程的统计,与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的。Transparent Huge Pages 缩写 THP ,这个是 RHEL 6 开始引入的一个功能,在 Linux6 上透明大页是默认启用的。由于 Huge pages 很难手动管理,而且通常需要对代码进行重大的更改才能有效的使用,因此 RHEL 6 开始引入了 Transparent Huge Pages ( THP ), THP 是一个抽象层,能够自动创建、管理和使用传统大页。THP 为系统管理员和开发人员减少了很多使用传统大页的复杂性 , 因为 THP 的目标是改进性能 , 因此其它开发人员 ( 来自社区和红帽 ) 已在各种系统、配置、应用程序和负载中对 THP 进行了测试和优化。这样可让 THP 的默认设置改进大多数系统配置性能。但是 , 不建议对数据库工作负载使用 THP 。这两者最大的区别在于 : 标准大页管理是预分配的方式,而透明大页管理则是动态分配的方式。
HugePages_Total:0预分配的可使用的标准大页池的大小。HugePages在内核中独立管理,只要一经定义,无论是否被使用,都不再属于free memory。Huge pages(标准大页) 是从 Linux Kernel 2.6 后被引入的,目的是通过使用大页内存来取代传统的 4kb 内存页面, 以适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能。
HugePages_Free:0标准大页池中尚未分配的标准大页
HugePages_Rsvd:0用户程序预申请的标准大页,尚未真的分配走
HugePages_Surp:0标准大页池的盈余
Hugepagesize:2048标准大页大小,这里是2M
DirectMap4k:89920映射为4kB的内存数量DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。所以DirectMap其实是一个反映TLB效率的指标。
DirectMap2M:4104192映射为2MB的内存数量
DirectMap1G:6291456映射为1GB的内存数量

4.3 根据meminfo统计内存占用

linux上的内存消耗总的来说有两部分,一部分是内核kernel进程,另一部分是用户进程。因此我们统计这两部分进程的内存消耗再相加即可。

  • 内核部分:Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X

X是指linux内核没有统计进来的,动态内存分配中通过alloc_pages分配的内存。http://linuxperf.com/?cat=7就指出了一个这样的例子: 在VMware guest上有一个常见问题,就是VMWare ESX宿主机会通过guest上的Balloon driver(vmware_balloon module)占用guest的内存,有时占用得太多会导致guest无内存可用,这时去检查guest的/proc/meminfo只看见MemFree很少、但看不出内存的去向,原因就是Balloon driver通过alloc_pages分配内存,没有在/proc/meminfo中留下统计值,所以很难追踪。

  • 用户进程部分:Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)Cached + AnonPages + Buffers + (HugePages_Total * Hugepagesize)

根据上述公式,除掉X部分,得出linux内核统计出来的已分配内存为:2.62G。

该值远小于使用freetop得到的used。 这里推测原因就是linux没有统计进来的alloc_pages分配的内存。 考虑到该linux确实是在VMWare平台上申请的虚拟机,因此我们推测是由于虚拟机平台内存不足,于是宿主机模拟该客户机内部进程消耗内存,实际上将内存调度到其他虚客户机去了。

五、结论

虚拟机管理员尚未最终确认,目前仅仅是推测

在VMWare虚拟机平台上,宿主机可以通过一个叫Balloon driver(vmware_balloon module)的驱动程序模拟客户机linux内部的进程占用内存,被占用的内存实际上是被宿主机调度到其他客户机去了。 但这种驱动程序模拟的客户机进程在客户机linux上是通过alloc_pages实现的内存动态分配,并没有被linux内核统计进来,于是造成了内存去向不明的现象。

原文章:

https://zhuanlan.zhihu.com/p/575366699

https://www.cnblogs.com/bakari/p/10486818.html

Sample数据分析:

使用方法:

命令:

python smaps_parser.py -f <name >

https://github.com/Gracker/Android-App-Memory-Analysis

脚本源码:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@Time  : 2019/6/13 上午10:55
#@Author: yangzhiting
#@File  : parse.pyimport argparse
import re
from collections import Counter
import os
import subprocesstype_length = 17
pssSum_count = [0] * type_length
pss_count = [0] * type_length
swapPss_count = [0] * type_lengthHEAP_UNKNOWN = 0
HEAP_DALVIK = 1
HEAP_NATIVE = 2HEAP_DALVIK_OTHER = 3
HEAP_STACK = 4
HEAP_CURSOR = 5 
HEAP_ASHMEM = 6
HEAP_GL_DEV = 7
HEAP_UNKNOWN_DEV = 8
HEAP_SO = 9
HEAP_JAR = 10 
HEAP_APK = 11
HEAP_TTF = 12
HEAP_DEX = 13
HEAP_OAT = 14
HEAP_ART = 15
HEAP_UNKNOWN_MAP = 16
HEAP_GRAPHICS = 17
HEAP_GL = 18
HEAP_OTHER_MEMTRACK = 19# Dalvik extra sections (heap)
HEAP_DALVIK_NORMAL = 20
HEAP_DALVIK_LARGE = 21
HEAP_DALVIK_ZYGOTE = 22
HEAP_DALVIK_NON_MOVING = 23# Dalvik other extra sections.
HEAP_DALVIK_OTHER_LINEARALLOC = 24
HEAP_DALVIK_OTHER_ACCOUNTING = 25
HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE = 26
HEAP_DALVIK_OTHER_APP_CODE_CACHE = 27
HEAP_DALVIK_OTHER_COMPILER_METADATA = 28
HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 29# Boot vdex / app dex / app vdex
HEAP_DEX_BOOT_VDEX = 30
HEAP_DEX_APP_DEX = 31
HEAP_DEX_APP_VDEX = 32# App art, boot art.
HEAP_ART_APP = 33
HEAP_ART_BOOT = 34_NUM_HEAP = 35
_NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1
_NUM_CORE_HEAP = HEAP_NATIVE+1#pss_type = ["HEAP_UNKNOWN", "HEAP_DALVIK", "HEAP_NATIVE", "HEAP_DALVIK_OTHER", "HEAP_STACK", "HEAP_CURSOR", "HEAP_ASHMEM", "HEAP_GL_DEV", \
#            "HEAP_UNKNOWN_DEV", "HEAP_SO", "HEAP_JAR", "HEAP_APK", "HEAP_TTF", "HEAP_DEX", "HEAP_OAT", "HEAP_ART", "HEAP_UNKNOWN_MAP" ,\
#            "HEAP_GRAPHICS","HEAP_GL","HEAP_OTHER_MEMTRACK",\
#           "HEAP_DALVIK_NORMAL,"HEAP_DALVIK_LARGE","HEAP_DALVIK_ZYGOTE","HEAP_DALVIK_NON_MOVING" ,\
#           "HEAP_DALVIK_OTHER_LINEARALLOC","HEAP_DALVIK_OTHER_ACCOUNTING","HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE","HEAP_DALVIK_OTHER_APP_CODE_CACHE","HEAP_DALVIK_OTHER_COMPILER_METADATA","HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE", \
#           "HEAP_DEX_BOOT_VDEX", "HEAP_DEX_APP_DEX","HEAP_DEX_APP_VDEX", \
#           "HEAP_ART_APP","HEAP_ART_BOOT" ]
pss_type = ["Unknown", "Dalvik", "Native", "Dalvik Other", "Stack", "Cursor", "Ashmem", "Gfx dev", \"Other dev", ".so mmap", ".jar mmap", ".apk mmap", ".ttf mmap", ".dex mmap", ".oat mmap", ".art mmap", "Other mmap", \"graphics","gl","other memtrack", \"dalvik normal","dalvik large","dalvik zygote","dalvik non moving" ,\"dalvik other lineralloc","dalvik other accounting","dalvik other zygote code cache","dalvik other app code cache","dalvik other compiler metadata","dalvik other indirect reference table" ,\"dex boot vdex","dex app dex","dex app vdex", \"heap art app","heap art boot"]
type_list = []
for i in range(type_length):type_list.append({})#制作提示
def help():parse = argparse.ArgumentParser(description="smaps parser")parse.add_argument('-p', '--pid', help="pid")parse.add_argument('-f', '--filename', help="smaps file")parse.add_argument('-t', '--type', help="Unknown, Dalvik, Native, Dalvik Other, Stack, Cursor, Ashmem, Gfx dev, \Other dev, .so mmap, .jar mmap, .apk mmap, .ttf mmap, .dex mmap, .oat mmap, .art mmap, Other mmap", default="ALL")parse.add_argument('-o', '--output', help="output file", default="smaps_analysis.txt")parse.add_argument('-s', '--simple', action="store_true", help="simple output", default=False)return parse.parse_args()def match_head(line):return re.match(r'(\w*)-(\w*) (\S*) (\w*) (\w*):(\w*) (\w*)\s*(.+)$', line, re.I)def match_type(name, prewhat):which_heap = HEAP_UNKNOWNsub_heap = HEAP_UNKNOWNis_swappable = Falseif(name.endswith(" (deleted)")):name = name[0 : len(name)- len(' (deleted)')]size = len(name)if name.startswith("[heap]"):which_heap = HEAP_NATIVEelif name.startswith("[anon:libc_malloc]"):which_heap = HEAP_NATIVEelif name.startswith("[anon:scudo:"):which_heap = HEAP_NATIVEelif name.startswith("[anon:GWP-ASan"):which_heap = HEAP_NATIVEelif name.startswith("[stack"):which_heap = HEAP_STACKelif name.startswith("[anon:stack_and_tls:"):which_heap = HEAP_STACKelif name.endswith(".so"):which_heap = HEAP_SOis_swappable = Trueelif name.endswith(".jar"):which_heap = HEAP_JARis_swappable = Trueelif name.endswith(".apk"):which_heap = HEAP_APKis_swappable = Trueelif name.endswith(".ttf"):which_heap = HEAP_TTFis_swappable = Trueelif name.endswith(".odex") | (size > 4 and name.__contains__(".dex")) :which_heap = HEAP_DEXsub_heap = HEAP_DEX_APP_DEXis_swappable = Trueelif name.endswith(".vdex"):which_heap = HEAP_DEX# Handle system@framework@boot and system/framework/boot|apexif name.__contains__("@boot") | name.__contains__("/boot") | name.__contains__("/apex"):sub_heap = HEAP_DEX_BOOT_VDEXelse:sub_heap = HEAP_DEX_APP_VDEXis_swappable = Trueelif name.endswith(".oat"):which_heap = HEAP_OATis_swappable = Trueelif name.endswith(".art") | name.endswith(".art]"):which_heap = HEAP_ART# Handle system@framework@boot* and system/framework/boot|apex*if name.__contains__("@boot") | name.__contains__("/boot") | name.__contains__("/apex"):sub_heap = HEAP_ART_BOOTelse:sub_heap = HEAP_ART_APPis_swappable = Trueelif name.startswith("/dev"):which_heap = HEAP_UNKNOWN_DEVif name.startswith("/dev/kgsl-3d0"):which_heap = HEAP_GL_DEVelif name.__contains__("/dev/ashmem/CursorWindow"):which_heap = HEAP_CURSORelif name.startswith("/dev/ashmem/jit-zygote-cache"):which_heap = HEAP_DALVIK_OTHERsub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHEelif name.__contains__("/dev/ashmem"):which_heap = HEAP_ASHMEMelif name.startswith("/memfd:jit-cache"):which_heap = HEAP_DALVIK_OTHERsub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHEelif name.startswith("/memfd:jit-zygote-cache"):which_heap = HEAP_DALVIK_OTHER;sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;elif name.startswith("[anon:"):which_heap = HEAP_UNKNOWNif name.startswith("[anon:dalvik-"):which_heap = HEAP_DALVIK_OTHERif name.startswith("[anon:dalvik-LinearAlloc"):sub_heap = HEAP_DALVIK_OTHER_LINEARALLOCelif name.startswith("[anon:dalvik-alloc space") | name.startswith("[anon:dalvik-main space"):# This is the regular Dalvik heap.which_heap = HEAP_DALVIKsub_heap = HEAP_DALVIK_NORMALelif name.startswith("[anon:dalvik-large object space") | name.startswith("[anon:dalvik-free list large object space"):which_heap = HEAP_DALVIKsub_heap = HEAP_DALVIK_LARGEelif name.startswith("[anon:dalvik-non moving space"):which_heap = HEAP_DALVIKsub_heap = HEAP_DALVIK_NON_MOVINGelif name.startswith("[anon:dalvik-zygote space"):which_heap = HEAP_DALVIKsub_heap = HEAP_DALVIK_ZYGOTE           elif name.startswith("[anon:dalvik-indirect ref"):sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLEelif name.startswith("[anon:dalvik-jit-code-cache") | name.startswith("[anon:dalvik-data-code-cache"):sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHEelif name.startswith("[anon:dalvik-CompilerMetadata"):sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATAelse:sub_heap = HEAP_DALVIK_OTHER_ACCOUNTINGelif not name.__eq__(" "):if name.__sizeof__() > 0:which_heap = HEAP_UNKNOWN_MAPelif prewhat == 10:which_heap = 10return which_heapdef match_pss(line):tmp = re.match('Pss:\s+([0-9]*) kB', line, re.I)if tmp:return tmpdef match_swapPss(line):tmp = re.match('SwapPss:\s+([0-9]*) kB', line, re.I)if tmp:return tmpdef parse_smaps(filename):file = open(filename, 'r')line = file.readline()if not line:returnwhat = 0prewhat = 0while 1:tmp = match_head(line)if tmp:name = tmp.group(8)# print("name:" + name)what = match_type(name, prewhat)# print "name = " + name + "what = " + str(what)while 1:line2 = file.readline()if not line2:returntmp2 = match_pss(line2)tmp3 = match_swapPss(line2)if tmp2 or tmp3:if what >= 0:if tmp2:pss = int(tmp2.group(1))pss_count[what] += pssif tmp3:pss = int(tmp3.group(1))swapPss_count[what] += pss# print("what:%d, pss:%d" % (what, pss))if pss > 0:pssSum_count[what] += psstmplist = type_list[what]if name in tmplist:tmplist[name] += psselse:tmplist[name] = psselse:tmp3 = match_head(line2)if tmp3:line = line2prewhat = whatbreakdef print_result(args):if args.pid and not args.output:output = "%d_smaps_analysis.txt" % pidelse:output = args.outputtype = args.typesimple = args.simpleindex = -1if not type == "ALL":if type in pss_type:index = pss_type.index(type)else:print("Please enter a correct memory type")returnoutput_file = open(output, 'w')if index == -1:for i,j,m,n,z in zip(pss_type, pssSum_count, pss_count, swapPss_count, type_list):tmp = "%s : %.3f M" % (i, float(j)/1000)print(tmp)output_file.write(tmp)output_file.write("\n")tmp = "\tpss: %.3f M" % (float(m) / 1000)print(tmp)output_file.write(tmp)output_file.write("\n")tmp = "\tswapPss: %.3f M" % (float(n) / 1000)print(tmp)output_file.write(tmp)output_file.write("\n")if not simple:count = Counter(z)for j in count.most_common():tmp = "\t\t%s : %d kB" % (j[0], j[1])print(tmp)output_file.write(tmp)output_file.write("\n")else:tmp = "%s : %.3f M" % (pss_type[index], float(pssSum_count[index]) / 1000)print(tmp)output_file.write(tmp)output_file.write("\n")tmp = "\tpss: %.3f M" % (float(pss_count[index]) / 1000)print(tmp)output_file.write(tmp)output_file.write("\n")tmp = "\tswapPss: %.3f M" % (float(swapPss_count[index]) / 1000)print(tmp)output_file.write(tmp)output_file.write("\n")if not simple:count = Counter(type_list[index])for j in count.most_common():tmp = "\t\t%s : %d kB" % (j[0], j[1])print(tmp)output_file.write(tmp)output_file.write("\n")if __name__ == "__main__":args = help()if args.filename:if os.path.exists(args.filename):parse_smaps(args.filename)print_result(args)else:print("smaps is not exist")elif args.pid:if args.pid.isdigit():pid = int(args.pid)if pid > 0:check_cmd = "adb shell su root ls /proc/%d/smaps >> /dev/null" % int(pid)ret = os.system(check_cmd)if ret == 0:cmd = "adb shell su root cat /proc/%d/smaps" % int(pid)smaps_filename = "%d_smaps_file.txt" % pidret = os.popen(cmd)new_file = open(smaps_filename, 'w')lines = ret.readlines()for line in lines:new_file.write(line)parse_smaps(smaps_filename)print_result(args)else:print("/proc/%d/smaps cannot be accessed" % pid)else:print("Please enter a correct pid")else:print("Please enter a correct pid")else:print("Please provide a pid or a smaps file")

参考网址:

http://light3moon.com/2020/12/07/Android%20%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96%E6%96%B9%E6%B3%95/

查看手机配置

可以通过这个命令查看heap的差异

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/10346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录第五十天|最佳买卖股票时机含冷冻期、买卖股票的最佳时机含手续费

题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 最佳买卖股票时机含冷冻期与打家劫舍的题目有异曲同工之妙&#xff0c;主要是出现了天数的间隔&#xff0c;一次需要在买卖股票的最佳时机II 题目上做一点调整&#xff0c;代码如下&#xff1a; 如代码所示&…

Linux系统编程——进程控制

目录 一&#xff0c;进程创建 1.1 fork回顾 1.2 写时拷贝 1.3 fork用处 1.4 fork调用失败原因 二&#xff0c;进程退出 2.1 进程退出场景 2.2 mainCRTStartup调用 2.3 进程退出码 2.3.1 main函数返回值 2.3.2 strerror ​编辑 2.3.3 命令的退出码 2.4 进程正常退…

【第19章】spring-mvc之全局异常处理

文章目录 前言一、全局异常处理1. 前端2. 后端 二、常见错误页1.增加界面2.web.xml3.异常处理4.效果 总结 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基…

CSS-伪类选择器

结构伪类选择器 作用&#xff1a;根据元素的结构关系查找元素 分类&#xff1a; 选择器说明元素名:first-child查找第一个元素元素名:last-child查找最后一个元素元素名:nth-child(N)查找第N名元素 <!DOCTYPE html> <html lang"en"> <head><me…

【北京迅为】《iTOP-3588从零搭建ubuntu环境手册》-第3章 Ubuntu20.04系统设置

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

【论文阅读笔记】jTrans(ISSTA 22)

个人博客地址 [ISSTA 22] jTrans&#xff08;个人阅读笔记&#xff09; 论文&#xff1a;《jTrans: Jump-Aware Transformer for Binary Code Similarity》 仓库&#xff1a;https://github.com/vul337/jTrans 提出的问题 二进制代码相似性检测&#xff08;BCSD&#xff0…

2024数维杯数学建模B题完整论文讲解(含每一问python代码+结果+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024数维杯数学建模挑战赛生物质和煤共热解问题的研究完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论…

数学学习笔记1——二次函数中的数形结合

二次函数中的数形结合 一、解一元二次不等式 基本方法&#xff1a;配方。 x 2 − 4 x 3 < 0 → ( x − 2 ) 2 < 1 → ∣ x − 2 ∣ < 1 → 1 < x < 3 x^2-4x3<0\to(x-2)^2<1\to\lvert x-2\rvert<1\to1<x<3 x2−4x3<0→(x−2)2<1→∣x−…

VBA_MF系列技术资料1-605

MF系列VBA技术资料1-605 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

具备教学意义的实操(用队列实现栈)

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/implement-stack-using-queues/description/ 实现逻辑 一个是先进先出&#xff08;队列&#xff09;&#xff0c;一个是后进先出&#xff08;栈&#xff09; 这里用两个队列导入一下数据…

项目管理 | 如何做好项目管理?

大部分人在做项目管理时会遇到以下问题&#xff1a; 团队沟通不畅&#xff0c;对于项目的各个环节和配合方无法掌控项目的任务分配和跟踪困难&#xff0c;项目进度不透明项目上线进度慢&#xff0c;没有威信难以服众 那项目管理怎么做&#xff1f;这篇就结合简道云团队的经验…

爬虫学习:XPath提取网页数据

目录 一、安装XPath 二、XPath的基础语法 1.选取节点 三、使用XPath匹配数据 1.浏览器审查元素 2.具体实例 四、总结 一、安装XPath 控制台输入指令&#xff1a;pip install lxml 二、XPath的基础语法 XPath是一种在XML文档中查找信息的语言&#xff0c;可以使用它在HTM…

数据结构----二叉树

博主主页: 码农派大星. 关注博主带你了解更多数据结构知识 1. 树型结构 1.1 概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上…

【软考】模拟考卷错题本2024-05-11

1 设计模式- 适配器模式 基本上上述的图解已经涵盖了绝大多数主流的设计模式和其特点。理解记忆下即可&#xff0c;这里对下午的考题也有帮助的。 2 计算机组成原理 cpu 访问速度 这个真的是憨憨咯~看到内存就选内存&#xff0c;题目都没审好。这里的速度比cpu内部的要比外部的…

c++ STL 之栈—— stack 详解

vector 是 stl 的一个关联容器,名叫“栈”&#xff0c;何为“栈”&#xff1f;其实就是一个数组&#xff0c;但有了数组何必还需栈&#xff0c;这是一个高深的问题。 一、简介 1. 定义 栈&#xff0c;是一个柔性数组&#xff08;可变长数组&#xff09;&#xff0c;可以变大变小…

Centos7安装图形化界面

前言&#xff1a;原文在我的博客网站中&#xff0c;持续更新数通、系统方面的知识&#xff0c;欢迎来访&#xff01; Centos7安装图形化界面https://myweb.myskillstree.cn/43.html 目录 一、安装GNOME桌面 二、开机自启动修改为命令行模式 三、卸载图形化界面 一、安装GN…

【C++】string类的使用③(修改器Modifiers || 非成员函数重载Non-member function overloads)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f525;修改器&#xff08;Modifiers&#xff09;**operator**appendpush_back和pop_backassigninserterasereplaceswap &#x1f525;非成员函数重载&#xff…

Java入门基础学习笔记4——开发Helloworld入门程序

Java程序开发的三个步骤&#xff1a; 1&#xff09;编写代码 2&#xff09;编译代码 3&#xff09;运行代码 注意事项&#xff1a; 第一个java程序建议使用记事本来编写。 建议代码文件名全英文、首字母大写、满足驼峰模式&#xff0c;源代码文件的后缀必须是.java 注意&a…

栈实现队列

一、分析 栈的特点是先出再入&#xff0c;而队列的特点为先入先出&#xff0c;所以我们创造两个栈&#xff0c;一个用来存放数据&#xff0c;一个用来实现其它功能此时栈顶为队尾&#xff1b;当要找队头数据时将前n-1个数据移入到另一个栈中&#xff0c;此时剩余那个数据为队头…

Sqlite在Mybatis Plus中关于时间字段的处理

我的个人项目中&#xff0c;使用Mybatis-Plus 和 Sqlite数据库&#xff0c; 但是在存储和查询时间字段的时候&#xff0c;总是出现问题&#xff0c;记录下我解决问题的过程。 Sqlite会默认把时间字段转成时间戳存储到数据库的字段中&#xff0c;看起来不直观&#xff0c;所以我…