/proc/meminfo memory状态解读
用途:可以整体的了解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 |
当前剩余的最大连续空间的大小 |
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
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进行打分,分数越高表示优先级越低,在内存紧张的时候就越容易被回收。 |
在实际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
FAQ22281 [Memory]如何查看gpu memory 讯息 |
名称 |
计算方式 |
如果相应值偏大,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 |
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 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
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
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结构成员对应。
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等 |
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
功能: 获取所有进程的内存使用的排行榜,排行是以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
process memory 占用量:
也写做VIRT或VSZ,它是进程使用的虚拟内存总量,不管是否有映射了物理内存。VSS在确认单个进程实际内存使用大小过程中用处不大。
比如malloc 一块大内存,实际上只分配了虚拟地址,并没有映射(消耗)物理地址,只有对这块内存访问时(比如 memeset)才会去映射物理内存。
Vss会高估实际内存使用量,因为程序通常会分配一些内存,但可能从来没用过。
当Process通过exec()类系统调用开始某个Program的执行时,Kernel 分配给Process的虚拟地址空间由以下内存区组成:
Kernel采用请求调页(demand paging)的内存分配策略。process可以在它的页还没在内存时就开始执行.
当process 访问一个不存在的页时,MMU产生一个异常;异常处理程序找到受影响的内存区,分配一个空闲的页,并用适当的数据进行初始化。
同样,process调用malloc 动态请求内存时,内核仅修改进程的堆内存区的大小。只有试图引用process的虚拟地址而产生异常时,才给进程分配页框。
也写作RES或者RSS, 这个是进程映射的物理内存数量。 用Rss来计算程序内存使用量,可能稍微好些,但是还是会有高估:因为它没有考虑到进程间共享的内存。
比如libc.so 在系统中只有一份内存copy,但是每个进程的Rss 都会把libc.so link到各进程的空间计算在内。
Pss 中除了独享的内存外,还会加上平均共享内存(共享的内存 除以 共享的进程数量,所以是平均值,也并不精确,占用多少跟共享内存大小及共享进程的数量有关)
PSS 是一个非常有用的数字,因为系统中全部进程以整体的方式被统计, 对于系统中的整体内存使用是一个很好的描述。
如果一个进程被终止, 其PSS 中所使用的共享库大小将会重新按比例分配给剩下的仍在运行并且仍在使用该共享库的进程。
此种计算方式有轻微的误差,因为当某个进程中止的时候,PSS没有精确的表示被返还给整个系统的内存大小。
Uss只统计进程的独享内存, 不包含与其他进程共享的内存。
比如A 和 B 进程都link 了libc.so, 那么A 或B进程统计Uss时,将把libc.so 占用的内存去掉。
Uss是一个非常非常有用的数据,因为它揭示了运行一个特定进程的真实内存增量大小
如果进程被终止,USS就是实际被返还给系统的内存大小。
USS是针对某个进程开始有可疑内存泄露的情况,进行检测的最佳数据。
我们假如进程的内存包括:
那么对于每个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 = 系统所有进程实际使用的物理内存
shared total :Tshare = Rss - Uss
share memory average: Ashare= Pss- Uss
通过上述数据可以获得该shared memory 区域的进程数 Nprocess= Tshare/Ashare
(zram 信息详见swap & swappiness一节。)
swap, 表示总共交换出去的size,和Rss相对应,基本上不看,用处不大
PSwap, pss中交换出去的size,和pss相对应(比例分配共享库占用的内存)。
USwap, 对应Uss,进程独自占用的物理内存中交换出去的size。
ZSwap, 使用zram 使用的大小。
reserved memory
MemTotal = DRAM_Size - Reserved memory.
Reserved memory = HW reserved + Kernel reserved
指令:
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 kxxx_64_bsp:/ # cat /proc/mtk_memcfg/reserve_memory |
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 |
关闭dfd |
+8M |
-MTK_DFD_ENABLE_CACHE_DUMP := yes |
|
amms_pos_size |
7.5M |
CMA free 可以被其它user使用,不做修改 |
|
CCCI_SMEM_SIZE_UDC_NONCACHE |
7.44M |
CMCC需求的3GPP feature. |
|
关闭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) |
|
SCP |
修改scp_share |
+10M |
reserve-memory-scp_share { |
scp |
+0.5M |
slim SCP.rar diff文件的修改,Q版本一般都已经包含,就只差一个 MTK_MINIMUM_SCP_DRAM_SIZE = yes 的配置。但不排除客户有自己的修改。 |
|
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)
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() |
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的大小之和,如下图所示,
5、获得各个function size后,选出使用量最大的进行分析,请相关owner确认是否有使用异常,或者是否可以瘦身。
Row Labels |
kernel-4.9 |
kernel-4.14 |
diff |
备注 |
_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 |
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.14的vmalloc usage page时,需去掉该部分的size |