android 内存分析(待续)

/proc/meminfo memory状态解读

  • 命令:adb shell cat /proc/meminfo
  • 内存分布log 查看方式

命令:adb shell cat /proc/meminfo

用途:可以整体的了解memory使用情况

我们说的可用memory一般以MemAvailable的数据为准。所以了解MemAvailable的组成可以帮助理解如何提升可用内存:

MemAvailable= free+filepage +slab_reclaimable -(lowmem_reserve +high_wmark_pages) -min(filepage/2,wmark_low) - min(slab_reclaimable/2,wmark_low)

其中:

lowmem_reserve为cat /proc/zoneinfo中的protection数据; 一般获取到的数据为0:   protection: (0, 0, 0)

high_wmark_pages 和 wmark_low 也是从zoneinfo中获取的high/low 水位

所以上述公式可以理解为: free, filepage, slab_reclaimable越大,且high_wmark_pages, wmark_low越小 ,则MemAvailable越多

Name

释义

Action

MemTotal

系统可用的总内存

(MemTotal = real_Dram_size - hw_reserved - kernel_reserved )

若MemTotal未达到预期,请查看reserved memory部分,check是否可以优化

MemFree

当前剩余的物理内存总量

free跟process数据有关,就算有被压缩,但其还是会mapping相关的memory,造成free比较少

 如果cached+MemFree <200M可能就会出现memory使用紧张的问题

MemAvailable

系统可用内存 , 包含已使用但可回收的 ,

如available = free+filepage +slab_reclaimable -(lowmem_reserve +high_wmark_pages) -min(filepage/2,wmark_low) - min(slab_reclaimable/2,wmark_low)

参见code: si_mem_available

其中lowmem_reserve为/proc/zoneinfo中的protection信息。

1、如果刚开机未达到开机可用内存目标,请查看开机memory数据获取,按照要求进行获取数据,然后再按照如下几个步骤进行check; 

1.1、check 本表格中MemTotal 是否达到预期

1.2、check 本表格中anon,file的用量比

1.3、check kernel memory分解, 排查kernel使用异常部分,并优化

1.4、check dumpsys meminfo分解 排查某个或某些内存占高的进程并优化

2、若是运行过程中出现low memory 问题, 则可以从降低单个process的memory使用量以及减少后台process的存活数量来着手,加强后台管理。具体请参考Low memory 处理建议

   

Buffers

Buffer是为了cpu和块设备(block device)之间读写速度不对等而设计的,Buffers统计的就是这部分缓冲区的内存总大小。这部分内存drop cache可以被回收。

Cached

用于文件高速缓存,不包括swapcache和buffers

即Cached = file pages-swapcache-buffers

约等于 Active(file) + Inactive(file)

cached统计了文件的缓存,其中有些文件当前被unmap, cache仍然可能保留它们,该部分可以被drop cache直接回收;而还有一部分被用户进程关联,比如shared libraries,mmap的文件等,这些缓存也就称为mapped. mapped是包含在cached里面,这部分是不可以被drop caches的.

cached都是file page,不是anon page, cached 和anon page之间没有重叠。

cached的大小一定程度上决定了MemAvailable的大小。所以若cached太小,请check anonpage和file page的比例

SwapCached

缓存的会swap出去的内容

Active

活跃的file page和匿名page

Active = Active(anon) + Active(file)

记录最近使用过的内存,通常不回收用于其它目的

Inactive

非活动的file page和匿名page

Inactive = Inactive(anon) + Inactive(file)

记录最近并没有使用过的内存,能够被回收用于其他目的

Active(anon)

活跃的匿名页

 如果anon数据较多,而file文件较少,请继续check swap Total和 SwapFree两个参数

Inactive(anon)

 不活跃的匿名页

Active(file)

 活跃的文件页

Inactive(file)

已经使用的不活跃的文件页

Unevictable

已经使用的不可回收的页.

包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(又被统计在”mlocked”中)、和ramfs。

Mlocked

通过mlock锁定在内存中当中的空间,不会被放到swap space当中

SwapTotal

swap分区的总量

1、针对低端机,可以适当加大swaptotal,如2G RAM 可以将其设置到1.3G左右

2、如果swapFree还剩余很多,且anon数据量大,请查看swappiness 的设置,并加大其设定的参数:

adb shell cat /proc/sys/vm/swappiness 

3、如果swapFree已经所剩无几,说明当前process较多,或者process所占内存较大。如果当前已处于低内存状态,请查看dumpsys meminfo分解 排查某个或某些内存占高的进程并优化

关于swap详细介绍请参考zram swap & swappiness

SwapFree

swap分区目前可用的量

Dirty

脏页的数量(就是文件内容有改变的页)

在磁盘缓冲区中尚未写入物理磁盘的内存大小

Writeback

 要被回写的内存页的大小

AnonPages

未映射文件的内存页数量

Linux内核中存在一个rmap(reverse mapping)机制,负责管理匿名内存中每一个物理内存页映射到哪个进程的哪个逻辑地址这样的信息。 这个rmap中记录的内存页总和就是AnonPages的值

Mapped

文件通过mmap分配的内存,用于map设备、文件或者库

Shmem

被各个进程共享的内存页的数量

tmpfs所使用的内存.tmpfs即利用物理内存来提供RAM磁盘的功能。在tmpfs上保存文件时,文件系统会暂时将它们保存到磁盘高速缓存上,因此它是属于磁盘高速缓存对应的"buffers+cached"一类。。但是由于磁盘上并没有与之对应的内容,因此它并未记录在File-backed内存对应的LRU列表上,而是记录在匿名内存的LRU表上。 这就是 buffers + cached = Active(file) + Inactive(file) + Shmem 公式的由来

被统计如kernel 占用内存,详见kernel memory分解

Slab

kernel数据结构的缓存大小,Slab=SReclaimable+SUnreclaim

Slab allocator是Linux kernel的内存分配机制,各内核子系统、模块、驱动程序都可以使用,但用完应该记得释放,忘记释放就会造成“内存泄露”(memory leak)。如果导致泄露的代码使用率很低倒也罢了,若是使用率很高的话,系统的内存会被迅速耗尽。

SReclaimable

Slab可被回收的量。

调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。

SUnreclaim

Slab中不可回收的量

KernelStack

内核线程栈占用的空间

PageTables

 该进程页表所占用的memory

NFS_Unstable

 不稳定页表的大小

Bounce

 有些老设备只能访问低端内存,比如16M以下的内存,当应用程序发出一个I/O 请求,DMA的目的地址却是高端内存时(比如在16M以上),内核将在低端内存中分配一个临时buffer作为跳转,把位于高端内存的缓存数据复制到此处。这种额外的数据拷贝被称为”bounce buffering”,会降低I/O 性能。大量分配的bounce buffers 也会占用额外的内存。

WritebackTmp

        

CommitLimit

 指当前可以分配给程序使用的虚拟内存

Committed_AS

 指当前已分配给程序使用的总虚拟内存

VmallocTotal

总分配的虚拟地址空间

VmallocUsed

 vmalloc已经使用的内存量

VmallocChunk

  当前剩余的最大连续空间的大小

内存分布log 查看方式

kernel log

<4>[366310.867958] DMA free:68160kB min:5140kB low:44156kB high:45996kB active_anon:126592kB inactive_anon:126772kB active_file:211508kB inactive_file:185592kB unevictable:5380kB writepending:1896kB present:1988536kB managed:1841596kB mlocked:5380kB slab_reclaimable:40648kB slab_unreclaimable:187980kB kernel_stack:49760kB pagetables:78368kB bounce:0kB free_pcp:2844kB local_pcp:696kB free_cma:0kB

  • DMA free:68160kB
    • 总剩余内存
  • min:5140kB low:44156kB high:45996kB 
  • active_anon:126592kB inactive_anon:126772kB
    • userspace process memory
  • active_file:211508kB inactive_file:185592kB
    • userspace file cache memory
  • managed:1841596kB
    • DRAM total减掉 reserved memory ,系统总共可运用的内存大小
  • slab_reclaimable:40648kB slab_unreclaimable:187980kB
    • slab 占用内存
  • kernel_stack:49760kB
    • kernel stack 占用内存, 可用来计算process数量 (32bit 8k per process , 64bit 16k per porcess)
  • 注意ion 和 gpu  无法由此看出, 可以通过dumpsys meminfo分解 部分进一步确认

dumpsys meminfo分解

  • 1、指令 dumpsys meminfo 
  • 2、dumpsys meminfo $pid

1、指令 dumpsys meminfo 

描述:可以了解系统详细的memory使用的情况,通过dumpsys meminfo 可以得到系统中各个process占用的内存情况,各种adj情况下的process的内存分布,总体的memory的使用统计等信息。区别于/proc/meminfo提供的内存数据,dumpsys meminfo更加偏向于andorid系统对于系统中memory使用情况的理解,因此我们在实际的memory的debug的过程中,在android系统会倾向使用dumpsys meminfo

执行结果:

名称

计算方式

next action

Total PSS by process

将系统中run的process的内存信息打印出来,通过该部分信息,可以清楚的看到每个process的内存使用情况

在实际debug的过程中,我们的如果怀疑某个process内存使用异常或者有leak的嫌疑,我们会以这个里面提供该process为基准进行相应的判断。。如果某个process的memory占用过大,或者一直增长,则可以通过dumpsys meminfo pid 来进一步check.

Total PSS by OOM adjustment

andorid系统的继承了部分linux 内存管理的做法,会在系统内存比较紧张或者在某些内存长期不使用的情况,会清掉优先级较低的进程,来让系统的具有良好的内存使用状况。对android系统我们的会对每个process进行打分,分数越高表示优先级越低,在内存紧张的时候就越容易被回收。
     1native process : native process的优先级一般很高,都是在0以下,内存很难被回收
     2Foreground 表示前台进程,优先级为0
     3Cached表示adj>=900 的进程,优先级比较低,在系统内存紧张的时候,可以随时被回收

在实际debug的过程中,我时常会遇到系统因为memory紧张导致重启的case,这种情况大都是native process 或者kernel里面有leak导致的。kernel 里面的leak大都是driver不合理的memory 使用导致的可以使用page owner来抓,判断native的process是否有leak,就是可以根据上述的native process里面是否有使用内存异常的process来判断。

total PSS 

cached pss + used pss   (cached pss : adj >=900 process pss sum , used pss : adj <900 process pss sum)

检查是否有占用memory异常多的process,然后再根据本文第二部分的dumpsys meminfo $pid的内容进一步确认单个process memory 占用异常的问题

total kernel 

cached kernel + kernel

确认kernel部分的占用情况

cached kernel

slab reclaimable + buffers + cached - mapped

kernel

 slab unreclaimable + shmem + vmalloc + page tables + kernel stack

参见kernel memory分解

EGL/GL

ION/GPU内存占用,可参考本文第二部分的dumpsys meminfo $pid 中graphics部分

ION部分请参考ION


GPU部分请参考:

FAQ22281 [Memory]如何查看gpu memory 讯息


2、dumpsys meminfo $pid

名称

计算方式

如果相应值偏大,next Action:

code 

.so private (clean+dirty) + .jar private (clean+dirty) + .apk private (clean+dirty) + .ttf private (clean+ dirty) + .dex private (clean + dirty) + .oat private (clean + dirty)

1、参考dumpsys 上面各个组成部分,定位是哪部分数据过大:.so/.jar/.apk/.ttf/.dex/.oat?

2、参考maps/smaps/showmap,使用showmap/smaps再具体定位是哪个文件,然后再请相应owner进一步确认或优化

Java heap

Dalvik private dirty+.art mmap private clean+.art mmap private dirty

抓取hprof 排查 heap 分布   (参考FAQ08893 如何抓取app 进程的hprof)

graphics

 GL mtrack private (clean + dirty) + EGL mtrack private(clean + dirty)

FAQ22290 [Memory]如何查看ion memory usage 
FAQ22281 [Memory]如何查看gpu memory 讯息
Quick Start > 内存泄漏专题分析 > native内存泄漏 > ion内存泄漏

native heap

native private dirty

Quick Start > 内存泄漏专题分析 > native内存泄漏

或参考 /development/scripts/native_heapdump_viewer.py    使用malloc debug +  adb shell am dumpheap $pid 查看 (具体参见后面介绍)

private other

private clean列+private dirty列-java heap-native heap-code-stack-graphic

PSS total

Pss Total列总和 + SwapPss Dirty列总和

stack 

stack private dirty 

system

total-private Clean 列总和+private dirty列总和

其中 .so,.jar,.apk,.ttf,.dex,.odex,.vdex,.oat,.art都是is_swappable的,其函数实现可参考:

/frameworks/base/core/jni/android_os_Debug.cpp    function: load_maps

Pss、Shared Dirty、Private Dirty这三列的数据是读取smaps文件生成。

下载 native_heapdump_viewer.py

/development/scripts/native_heapdump_viewer.py 介绍:

30 1. Collect a native heap dump from the device. For example:

31 $ adb shell stop

32 $ adb shell setprop libc.debug.malloc.program app_process

33 $ adb shell setprop libc.debug.malloc.options backtrace=64

34 $ adb shell start (launch and use app)

36 $ adb shell am dumpheap -n <pid> /data/local/tmp/native_heap.txt

37 $ adb pull /data/local/tmp/native_heap.txt

38

39 2. Run the viewer:

40 $ python native_heapdump_viewer.py [options] native_heap.txt

41 [--verbose]: verbose output

42 [--html]: interactive html output

43 [--reverse]: reverse the backtraces (start the tree from the leaves)

44 [--symbols SYMBOL_DIR] SYMBOL_DIR is the directory containing the .so files with symbols.

45 Defaults to $ANDROID_PRODUCT_OUT/symbols

46 [--app-symbols SYMBOL_DIR] SYMBOL_DIR is the directory containing the app APK and so files.

47 Defaults to the current directory.

48 This outputs a file with lines of the form:

49

50 5831776 29.09% 100.00% 10532 71b07bc0b0 /system/lib64/libandroid_runtime.soTypeface_createFromArray frameworks/base/core/jni/android/graphics/Typeface.cpp:68

51

52 5831776 is the total number of bytes allocated at this stack frame, which

53 is 29.09% of the total number of bytes allocated and 100.00% of the parent

54 frame's bytes allocated. 10532 is the total number of allocations at this

55 stack frame. 71b07bc0b0 is the address of the stack frame.

maps/smaps/showmap

  • 1、maps
  • 2、smaps
  • 3、showmap

Linux采用虚拟内存技术管理process 地址空间. 将process memory分成不同的内存区。每个区域具有各自的访问属性,大小是4k的倍数。

代码实现上使用vm_area_struct节点串成链表。
我们可以从maps & smaps里面获取到process detail的memory信息。

参考link: kernel document  https://www.kernel.org/doc/Documentation/filesystems/proc.txt 

1、maps

adb shell cat /proc/pid/maps

77bed25000-77bed26000 rw-p 00013000 fd:04 1434                           /vendor/lib64/libgpu_aux.so

该文件有6列:

地址(77bed25000-77bed26000):地址范围

权限(rw-p):虚拟内存的权限,r=读,w=写,x=执行,s=共享,p=私有;

偏移量(00013000):在进程里地址偏移量

设备(fd:04):映像文件的主设备号和次设备号,可以通过通过 cat /proc/devices查看设备号对应的设备名

节点(1434):映像文件的节点号;

路径(/vendor/lib64/libgpu_aux.so): 映像文件的路径

每项都与一个vm_area_struct结构成员对应。

2、smaps

adb shell cat /proc/pid/smaps

smaps是对maps 信息的详细描述。描述了每个虚拟内存区域的size等信息。 

00400000-0048a000 r-xp 00000000 fd:03 960637 /bin/bash // 该行同maps中的信息
Size: 552 kB //指vss, 以及后面RSS PSS释义请参见procrank 分解及VSS/RSS/PSS/USS基础知识
Rss: 460 kB 
Pss: 100 kB 
Shared_Clean: 452 kB //映射中已被此进程引用的页面,以及至少一个其他进程,但不是由任何进程编写的;
Shared_Dirty: 0 kB //映射中已被此进程引用的页面,并且至少由其中一个进程编写; 其中dirty页如果没有交换机制的情况下,应该是不能回收的
Private_Clean: 8 kB //映射中已读取但未由此进程写入但未被任何其他进程引用的页面;
Private_Dirty: 0 kB //映射中已由此进程写入但未被任何其他进程引用的页面.
Referenced: 460 kB //表示当前标记为已引用或者已访问的内存量
Anonymous: 0 kB //不属于任何文件的内存量
AnonHugePages: 0 kB 
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
Swap: 0 kB //显示多少潜在的已经被使用但没有被交换的匿名内存内存页
KernelPageSize: 4 kB //kernel用来支持虚拟内存区域的页面大小。一般与MMU使用大小相匹配。
MMUPageSize: 4 kB //被MMU使用的页面大小
Locked: 0 kB //表示映射是否被锁定在内存中
ProtectionKey: 0
VmFlags: rd ex mr mw me dw 

根据smaps中每一项的object内容,可以把memory分成不同的category,借助其他统计工具可以统计出每种类型Memory的用量。

.so

cmdline: => contains .so

native lib. RO + RW

boot.oat + boot.art

cmdline: => contains .oat or contains .art

preloaded classes which are commonly used by multiple apps, 例如:core-libart.jar, conscrypt.jar, okhttp.jar, core-junit.jar, bouncycastle.jar, ext.jar, framework.jar

telephony-common.jar, android.policy.jar, apache-xml.jar

dex

cmdline: => contains dex

Process本身依赖的一些java模块对应的dex

Native (Heap)

cmdline: => contains heap, contains libc_malloc, contains linker_alloc, or contains sigpage

stack

cmdline: => contains stack and does not contain anon

anon

cmdline: => contains anon

unknown

Java

cmdline: => contains dalvik

Java Heap. 由App本身需要的Heap以及Zygote所占用的Heap两部分组成。

Others

perm: => contains w or contains x

copy-on-write mapping

perm: => ---p

Resource

其他不知如何归类的认为是resource. 例如font, apk等

3、showmap 

adb shell system/bin/showmap $pid 

功能:可以用来定位占用memory比较大的资源模块。定位后可以根据实际情况找相关模块确认是否合理。

virtual                     shared  shared  private  private
  size   RSS  PSS       clean     dirty     clean   dirty    swap swapPSS #  object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
128     4       0            0          4          0         0         0        0        1  /dev/__properties__/properties_serial
152   152     0            0         152        0         0         0        0        2  /dev/__properties__/property_info
128     4       2            0          4          0         0         0        0        1  /dev/__properties__/u:object_r:apexd_prop:s0

我们一般用PSS+SwapPss来的求和来计算该文件占用的memory大小。


showmap和smaps的关系

实际是读取上述smaps中的数据然后规整分类,测试中showmap和cat/proc/PID/smaps抓取要连续,间隔久了memory会有变化

showmap中看到的是smaps中对应binary r--p/--xp/rw-p/rw-p所有项的聚合,是share lib size or execuatable size;
smaps中--xp是code size, 其他项都是data size

procrank 分解及VSS/RSS/PSS/USS基础知识

  • 1、指令: procrank
  • 2、VSS/RSS/PSS/USS基础知识
    • 2.1 VSS 
      • Process 虚拟地址空间处理
    • 2.2 RSS
    • 2.3 PSS
    • 2.4 USS
    • 2.5 图示
    • 2.6 番外篇
  • 3、swap/PSwap/USwap/ZSwap

1、指令: procrank

功能: 获取所有进程的内存使用的排行榜,排行是以Pss的大小而排序。

            procrank命令比dumpsys meminfo命令,能输出更详细的VSS/RSS/PSS/USS内存指标。

    其数据是从proc/[pid]/smaps中统计出来的,

注意: 该指令使用前需先 root

执行结果:

k69v1_64:/ # procrank
PID     Vss           Rss         Pss          Uss          Swap    PSwap  USwap  ZSwap cmdline
1163 8301540K 292500K 134167K 115232K    18032K    5372K   1228K 1226K  system_server
2036 6239616K 238808K 94109K   45468K       8452K    1935K     596K  441K   com.google.android.gms.persistent
2447 6299948K 222520K 80168K   31768K     24008K    8400K     2164K 1917K  com.google.android.gms
5548 6612676K 223860K 79406K   53324K     20948K    8504K     3376K 1941K  com.google.android.googlequicksearchbox
search
7372 5935688K 196336K 71705K 60224K        3824K      86K         0K       19K     com.android.settings
1320 6915696K 212372K 66895K 54108K       20620K    7813K    2868K   1783K   com.android.systemui
1748 5705716K 162736K 62378K 54820K          0K           0K         0K         0K      com.google.android.inputmethod.latin
.......

------ ------ ------ ------ ------ ------ ------
1329992K 906904K 427204K 87374K 19184K 19948K TOTAL

ZRAM: 71488K physical used for 313192K in swap (2157132K total swap)
RAM: 3923480K total, 733604K free, 63408K buffers, 1929524K cached, 3792K shmem, 172472K slab

2、VSS/RSS/PSS/USS基础知识

process memory 占用量:

2.1 VSS 

也写做VIRT或VSZ,它是进程使用的虚拟内存总量,不管是否有映射了物理内存。VSS在确认单个进程实际内存使用大小过程中用处不大。

比如malloc 一块大内存,实际上只分配了虚拟地址,并没有映射(消耗)物理地址,只有对这块内存访问时(比如 memeset)才会去映射物理内存。
Vss会高估实际内存使用量,因为程序通常会分配一些内存,但可能从来没用过。

Process 虚拟地址空间处理

 当Process通过exec()类系统调用开始某个Program的执行时,Kernel 分配给Process的虚拟地址空间由以下内存区组成:

  • program的可执行代码
  • program的初始化数据
  • program的未初始化数据
  • 初始程序stack
  • 共享库的可执行代码和数据
  • heap

Kernel采用请求调页(demand paging)的内存分配策略。process可以在它的页还没在内存时就开始执行.
当process 访问一个不存在的页时,MMU产生一个异常;异常处理程序找到受影响的内存区,分配一个空闲的页,并用适当的数据进行初始化。
同样,process调用malloc 动态请求内存时,内核仅修改进程的堆内存区的大小。只有试图引用process的虚拟地址而产生异常时,才给进程分配页框。

2.2 RSS

也写作RES或者RSS, 这个是进程映射的物理内存数量。 用Rss来计算程序内存使用量,可能稍微好些,但是还是会有高估:因为它没有考虑到进程间共享的内存。
比如libc.so 在系统中只有一份内存copy,但是每个进程的Rss 都会把libc.so link到各进程的空间计算在内。

2.3 PSS

Pss 中除了独享的内存外,还会加上平均共享内存(共享的内存 除以 共享的进程数量,所以是平均值,也并不精确,占用多少跟共享内存大小及共享进程的数量有关)

PSS 是一个非常有用的数字,因为系统中全部进程以整体的方式被统计, 对于系统中的整体内存使用是一个很好的描述。
如果一个进程被终止, 其PSS 中所使用的共享库大小将会重新按比例分配给剩下的仍在运行并且仍在使用该共享库的进程。
此种计算方式有轻微的误差,因为当某个进程中止的时候,PSS没有精确的表示被返还给整个系统的内存大小。

2.4 USS

Uss只统计进程的独享内存, 不包含与其他进程共享的内存。
比如A 和 B 进程都link 了libc.so, 那么A 或B进程统计Uss时,将把libc.so 占用的内存去掉。

Uss是一个非常非常有用的数据,因为它揭示了运行一个特定进程的真实内存增量大小

如果进程被终止,USS就是实际被返还给系统的内存大小。

USS是针对某个进程开始有可疑内存泄露的情况,进行检测的最佳数据。

2.5 图示

我们假如进程的内存包括:

  • A = 映射到物理内存的共享内存,这个会包含部分active的stack空间和heap空间。
  • B = 进程间映射和共享空间,比如shared libraries
  • C = 分配但是从未touch的虚拟地址空间。

那么对于每个Process:
Vss = A + B + C 
Rss = A + B 
Uss = A 
Pss = A + B/n , n是共享这个内存的Process的数量 

假如系统中有如下3个process,使用内存如下:


Pss(1) = 2 + 3/3 + 2/2 = 4 
Pss(2) = 2 + 3/3 + 2/2 = 4 
Pss(3) = 2 + 3/3 = 3 
Sum(Pss) = 11 = 系统所有进程实际使用的物理内存

2.6 番外篇

shared total :Tshare = Rss - Uss

share memory average: Ashare= Pss- Uss

通过上述数据可以获得该shared memory 区域的进程数 Nprocess= Tshare/Ashare

3、swap/PSwap/USwap/ZSwap

       (zram 信息详见swap & swappiness一节。)

swap,     表示总共交换出去的size,和Rss相对应,基本上不看,用处不大
PSwap,   pss中交换出去的size,和pss相对应(比例分配共享库占用的内存)。
USwap,   对应Uss,进程独自占用的物理内存中交换出去的size。
ZSwap,   使用zram 使用的大小。

reserved memory

  • 获取reserved memory的方式:
    • 1、 P版本或之前:
    • 2、 Q版本及以上:
  • 相关code:
  • 优化方式

MemTotal = DRAM_Size - Reserved memory.

Reserved memory = HW reserved + Kernel reserved

获取reserved memory的方式:

1、P版本或之前:

指令:   

adb shell cat /proc/mtk_memcfg/total_reserve

adb shell cat /proc/mtk_memcfg/reserve_memory

示例:

kxxx_64_bsp:/ # cat /proc/mtk_memcfg/total_reserve
230084 kB

kxxx_64_bsp:/ # cat /proc/mtk_memcfg/reserve_memory
*mblock-15-ccci: 77856768
mblock-9-dtb_kernel_addr_mb: 524288
ram_console-reserved-memory@47c80000: 65536
pstore-reserved-memory@47c90000: 917504
minirdump-reserved-memory@47d70000: 65536
*mblock-4-atf-reserved: 327680
mblock-7-pl-bootarg: 2097152
*mblock-8-lk_addr_mb: 4194304
*mblock-12-SPM-reserved: 65536
*mblock-16-ccci: 23068672
*mblock-14-ccci: 1507328
*mblock-10-framebuffer: 14942208
*mblock-6-SSPM-reserved: 786432
mblock-3-log_store: 262144
*reserve-memory-scp_share: 3145728
*reserve-memory-sspm_share: 5308416
*mblock-13-SCP-reserved: 6291456
*consys-reserve-memory: 4194304
*wifi-reserve-memory: 3145728
*mblock-11-atf-ramdump-memory: 524288
*mblock-5-atf-log-reserved: 262144
*mblock-1-dramc-rk0: 4096
*mblock-2-dramc-rk1: 4096
kernel(text): 14088192
kernel(data): 13785684
kernel(page): 50331584
kernel(other): 7839212

2、 Q版本及以上:

    2.1 HW reserved memory:

           使用tool: memory-layout-parser tool  (request tool by e-service)

output in excel format 

SUMMARY

Type

Size (KB)

ccci

95872

md_smem

24576

scp

19456

framebuffer

14464

sspm

5952

consys

4096

lk

4096

wifi

3072

pl

2048

atf

1152

pstore

896

dtb

512

log_store

256

minirdump

64

ram_console

64

spm

64

ion

16

dram

4

Total

176660

HW reserved的优化,可以比较测试机和对比机的各个部分的差异,以Q平台为例:

HW reserved Type

phone-A

Size (KB)

Phone-B

Size (KB)

diff(B-A)

atf

832

1088

256

ccci

67392

77632

10240

consys

4096

4096

0

dram

8

4

-4

dtb

512

512

0

framebuffer

16000

16320

320

lk

4096

9216

5120

log_store

256

256

0

md_smem

1472

1472

0

minirdump

64

64

0

pl

2048

2048

0

pstore

896

896

0

ram_console

64

64

0

scp

4608

9216

4608

sspm

1856

5952

4096

spm

64

64

0

tee

51456

51200

-256

wifi

3072

3072

0

unknown type

 

 

0

 total

155.07

178.88

23.81

比较后,可以根据如下HW reserved表格中方案按实际情况和需求修改对应的偏差较大的部分:

HW reserved

memory 大小

修改方案

ccci

关闭ccb buffer

将ccb buffer设置成12M

(因设置成0M会导致Meta连接不上的问题)

+10M

MTK_DYNAMIC_CCB_BUFFER_GEAR_ID

Option Value CCB Size Comments
1 22MB  
2 12MB  (推荐选择这个)
3 0MB 这个即为关闭CCB,但是会有meta连不上的问题
11 4MB 
只改project config,有可能不生效,需要具体再看。

关闭dfd

+8M

-MTK_DFD_ENABLE_CACHE_DUMP := yes
+MTK_DFD_ENABLE_CACHE_DUMP := no

amms_pos_size

7.5M

CMA free 可以被其它user使用,不做修改

CCCI_SMEM_SIZE_UDC_NONCACHE

7.44M

CMCC需求的3GPP feature.
内部测试机默认R3,而客户开案是在LR12A.R2.TC16.Q.SP該Branch並沒有UDC

关闭C2K(CDMA2000)

+7MB

申请modem patch,参考CR ALPS05131754

 IMS:support 40 dialog ->10 dialog

+4M

申请modem patch,参考CR ALPS05131754

需确认modem版本,如Gen93:

IMS_GEN_CFG = GEN93_ENG_SLIM (for L+L)
IMS_GEN_CFG = GEN93_ENG_SLIM_NO_MIMS (for L+W)
MULTIPLE_IMS_SUPPORT = FALSE (for L+W)

SCP

修改scp_share

+10M

reserve-memory-scp_share {
compatible = "mediatek,reserve-memory-scp_share";
no-map;
- size = <0 0x00d00000>;
+ size = <0 0x00300000>;
alignment = <0 0x1000000>;
alloc-ranges = <0 0x40000000 0 0x50000000>;
};

scp

+0.5M

slim SCP.rar

diff文件的修改,Q版本一般都已经包含,就只差一个 MTK_MINIMUM_SCP_DRAM_SIZE = yes 的配置。但不排除客户有自己的修改。
 MTK_MINIMUM_SCP_DRAM_SIZE = yes (at vendor/mediatek/proprietary/bootable/bootloader/lk/) 

sspm

Using dynamic allocate memory for MET 

+4M

slim SSPM.diff.zip  

framebuffer

Framebuffer的size与LCM 的width/height有关

1、如phone-B用的是1080*1920的LCM, Phone-A用的是720*1080的LCM.

2、这个size还包含Assert layer

贵司可以根据项目的LCM的size自定义framebuffer的大小。

另外非DRM chip 不需要优化,因为framebuffer进入kernel之后,将转换成FB heap,给Gralloc使用,所以不算浪费。

2.2     kernel reserved memory:

进入linux服务区,在build 的load路径下out\target\product\[project_name]\obj\KERNEL_OBJ下找到vmlinux 文件,然后在该路径下执行指令:

  nm vmlinux |grep  -e '_etext' -e '_stext' -e '_edata' -e '\b_sdata' -e '__end_rodata' -e '__start_rodata' -e '__bss_stop' -e '__bss_start' -e '__init_end' -e '__init_begin' -e '_einittext' -e '_sinittext' | perl -e 'while (<>){ chomp($_); if ($_ =~ /([a-f0-9]+) . _etext/) { $_etext = $1} elsif ($_ =~ /([a-f0-9]+) . _stext/) { $_stext = $1} elsif ($_ =~ /([a-f0-9]+) . _edata/) { $_edata = $1} elsif ($_ =~ /([a-f0-9]+) . _sdata/) { $_sdata = $1} elsif ($_ =~ /([a-f0-9]+) . __end_rodata/) { $__end_rodata = $1} elsif ($_ =~ /([a-f0-9]+) . __start_rodata/) { $__start_rodata = $1} elsif ($_ =~ /([a-f0-9]+) . __bss_stop/) { $__bss_stop = $1} elsif ($_ =~ /([a-f0-9]+) . __bss_start/) { $__bss_start = $1} elsif ($_ =~ /([a-f0-9]+) . __init_end/) { $__init_end = $1} elsif ($_ =~ /([a-f0-9]+) . __init_begin/) { $__init_begin = $1} elsif ($_ =~ /([a-f0-9]+) . _einittext/) { $_einittext = $1} elsif ($_ =~ /([a-f0-9]+) . _sinittext/) { $_sinittext = $1}} $codesize = hex($_etext) - hex($_stext); print "codesize=" .int($codesize/1024) . "K\n"; $datasize = hex($_edata) - hex($_sdata); print "datasize=" .int($datasize/1024) ."K\n"; $rosize = hex($__end_rodata) - hex($__start_rodata); print "rosize=" .int($rosize/1024) . "K\n"; $bss_size = hex($__bss_stop) - hex($__bss_start); print "bss=" .int($bss_size/1024) . "K\n"; $init_data_size = hex($__init_end) - hex($__init_begin); print "init_data_size=" .int($init_data_size/1024) ."K\n"; $init_code_size = hex($_einittext) - hex($_sinittext); print "init_code_size=" .int($init_code_size/1024) ."K\n"'

得到的结果:

codesize=14526K

datasize=1986K

rosize=4972K

bss=2733K

init_data_size=3904K

init_code_size=455K

kernel(text) = codesize

kernel(data) = datasize+rosize+bss

kernel(init) = init_data_size

跟kernel log中dump是一致的,所以获取Kernel reserved memeory的状况二选一就好

Memory: 1726420K/2056764K available (14526K kernel code, 1986K rwdata, 4972K rodata, 3904K init, 2733K bss, 330344K reserved, 0K cma-reserved)

相关code:

How to reserved memory?

--->use mblock_reserve or mblok_reserve_ext in lk 

ex:

/vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt6755/

A D

platform.c

546 addr = mblock_reserve(&g_boot_arg->mblock_info, 0x2100000, 0x100000, 0xa0000000, RANKMAX); in mblock_create_test() 
671 g_fb_base = mblock_reserve(&g_boot_arg->mblock_info, g_fb_size, 0x200000, 0x100000000, RANKMAX); in platform_init() 
674 g_fb_base = mblock_reserve(&g_boot_arg->mblock_info, g_fb_size, 0x100000, 0x100000000, RANKMAX); in platform_init() 

2. dts config

ex:

242 	reserved_memory: reserved-memory {
243 		#address-cells = <2>;
244 		#size-cells = <2>;
245 		ranges;
246 
247 		reserve-memory-sspm_share {
248 			compatible = "mediatek,reserve-memory-sspm_share";
249 			no-map;
250 			status = "okay";
251 #ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
252 			size = <0 0x110000>; /* 1M + 64K */
253 #else
254 			size = <0 0x510000>; /* 5M + 64K */
255 #endif
256 			alignment = <0 0x10000>;
257 			alloc-ranges = <0 0x40000000 0 0x60000000>;
258 		};
259 
260 		reserve-memory-scp_share {
261 			compatible = "mediatek,reserve-memory-scp_share";
262 			no-map;
263 			size = <0 0x00300000>;
264 			alignment = <0 0x1000000>;
265 			alloc-ranges = <0 0x40000000 0 0x50000000>;
266 		};
267 
268 		consys-reserve-memory {
269 			compatible = "mediatek,consys-reserve-memory";
270 			no-map;
271 			size = <0 0x400000>;
272 			alignment = <0 0x1000000>;
273 			alloc-ranges = <0 0x40000000 0 0x80000000>;
274 		};
275 
276 		wifi-reserve-memory {
277 			compatible = "mediatek,wifi-reserve-memory";
278 			no-map;
279 			size = <0 0x300000>;
280 			alignment = <0 0x1000000>;
281 			alloc-ranges = <0 0x40000000 0 0x80000000>;
282 		};

优化方式

1 其他feature需相应owner来一一check优化(对比)

2 MD需MD PL列出相应的feature,PM根据市场需求来决定开启/关闭哪些feature,如前文中Hw reserved表格中所列优化项。以达到优化的目的

3 kernel  code/data相关的部分:

     3.1   先从 linux/bloat-o-meter at master · torvalds/linux · GitHub 获取最新的bloat-o-meter script

     3.2   Comparing code/data/bss size of vmlinux by bloat-o-meter

Linux provides a script to compare two vmlinux and generates report for them.

Usage: latest bloat-o-meter has options such as -t (text), -d (data), and -c (categorized output).

./script/bloat-o-meter vmlinux-old vmlinux-new

output of report

add/remove: 2062/1304 grow/shrink: 23801/2790 up/down: 2133305/-367564 (1765741)
Function                                     old     new   delta
RGXDumpRGXRegisters                            -   18196  +18196
musb_ep_program                                -   12740  +12740
uvc_probe                                      -    8648   +8648
v4l2_ctrl_get_name                             -    7008   +7008
update_blocked_averages                     2088    8820   +6732
ISP_Buf_CTRL_FUNC                           9732   15888   +6156
...
Total: Before=11226411, After=12992152, chg +15.73%

kernel memory分解

kernel total =MEMINFO_SHMEM+MEMINFO_SLAB+MEMINFO_VM_ALLOC_USED+MEMINFO_PAGE_TABLES+MEMINFO_KERNEL_STACK

Kernel

备注

MEMINFO_SHMEM

Shmem统计的内容包括:

·       shared memory:

·       SysV shared memory [shmget etc.]

·       POSIX shared memory [shm_open etc.]

·       shared anonymous mmap [ mmap(…MAP_ANONYMOUS|MAP_SHARED…)]

·       tmpfs和devtmpfs。

注:所有tmpfs类型的文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件在没有关闭的情况下被删除,空间仍然不会释放,shmem不会减小

MEMINFO_SLAB

同process 数量有关,则可考虑减少process or task

·       FAQ21613 [Memory]如何分析slab占用内存细节以及slab leak

·       FAQ21615 [Memory]如何查询内核所有 page 的使用情况 (by page owner)

MEMINFO_VM_ALLOC_USED

(All size in adb shell cat /proc/vmallocinfo exclude lines with "ioremap" / "map_lowmem" / "vm_map_ram" / “unpurged”pattern)

最新结果表明,kernel-4.14上 unpurged 也是可用部分,所以也应在计算时去除

MEMINFO_PAGE_TABLES

用于将内存的虚拟地址翻译成物理地址。随着内存地址分配的越来越多,page table 会增大。

(Page Table的用途是翻译虚拟地址和物理地址,它是会动态变化的,要从MemTotal中消耗内存)

同process or task 数量有关, 数据太大,则可考虑减少process or task

MEMINFO_KERNEL_STACK

每一个用户线程都会分配一个kernel stack(内核栈),内核栈虽然属于线程,但用户态的代码不能访问,只有通过系统调用(syscall)、自陷(trap)或异常(exception)进入内核态的时候才会用到,也就是说内核栈是给kernel code使用的。

Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里。所以属于kernel消耗的内存。

上面的数据除MEMINFO_VM_ALLOC_USED外,其它几项都来自于adb shell cat /proc/meminfo。所以本部分主要介绍MEMINFO_VM_ALLOC_USED的计算:

1、adb shell cat /proc/vmallocinfo > SYS_VMALLOC_INFO

2、用TextAnalysisTool.NET工具打开获取到的SYS_VMALLOC_INFO 文件,然后按“ctrl+N”键去除显示"ioremap" / "map_lowmem" / "vm_map_ram" / “unpurged”几个关键字

3、然后将剩下的内容全部copy到excel中,然后点选 数据→分列->选择分隔符号->勾选"空格”和“其他:+”->完成

4、统计所有function size的大小之和,如下图所示,

  • 先在首行插入一行,填写每列所代表的释义
  • 选取 D列和E列(size 大小列和function name 列)->插入->数据透视表->选择防止数据透视表位置->现有工作表,然后任意选择空白区域→确定

  • 在右边的数据透视表字段中,勾选size到值,勾选function到行,然后在左边的数据表格中,双击求和项,选择求和。就可以显示每个function用掉的memory size了。

5、获得各个function size后,选出使用量最大的进行分析,请相关owner确认是否有使用异常,或者是否可以瘦身。

番外篇:关于kernel-4.9 vs kernel-4.14  vmalloc usage

    

Row Labels

kernel-4.9
sum of size(KB)

kernel-4.14
Sum of size(KB)

diff
(KB)

备注

_do_fork

0

53180

53180

kernel-4.14有开启CONFIG_VMAP_STACK 这个功能,会把thread alloc计算到vmalloc中,该功能请参考

Several ARM64 Changes Queued For Linux 4.14, VMAP_STACK Support - Phoronix

基本上是多一个 stack overflow 的debug机制。

如不需要,则可以去掉CONFIG_VMAP_STACK的配置:

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig

select HAVE_ARCH_TRANSPARENT_HUGEPAGE
- select HAVE_ARCH_VMAP_STACK
select HAVE_ARM_SMCCC

unpurged

0

50308

50308

 unpurged 是kernel-4.14 上 remove vm area 的一个过渡状态, 在kernel 4.9 remove_vm_area 时直接把VM_VM_AREA 清掉(va->flags &= ~VM_VM_AREA;)

但是Kernel-4.14 也会清掉VM_VM_AREA , 但隨会标记成VM_LAZY_FREE 所以后面会被统计到unpurged

所以可以说是统计方法不同而已  (一个直接remove了, 另一個被统计到unpurged )

所以在计算kernel-4.14vmalloc usage page时,需去掉该部分的size

猜你喜欢

转载自blog.csdn.net/yangzex/article/details/127991672