(0). Android/Linux 記憶體分配的兩個重要策略.

Linux 在分配記憶體時, 為了節省記憶體, 按需分配, 使用了延時分配以及Copy-On-Write 的策略。

延時分配即針對user space 申請memory 時, 先只是明面上的分配虛擬空間, 等到真正操作memory 時, 才真正分配具體的物理記憶體, 這個需要藉助MMU 的data abort 轉換成page fault 來達成。 這樣就可以極大的避免因user space 過度申請memory, 或者錯誤申請memory 造成的memory 浪費。

而Copy-On-Write 即是在程序fork 時, 子程序和父程序使用同一份memory, 只有當某塊memory 被更新時, 才重新copy 出新的一份。 這個在android 上表現也非常顯著, 上層app 包括system server 都由zygote fork 出來, 並且沒重新exec 新的bin, ART VM/Lib 的memory 都是共享的, 可以極大的節省Memory 的使用。

對應的我們在評估一個程序的memory 使用時, 我們往往就需要觀察它使用的虛擬的memory 空間, 它真實的使用的物理memory, 它和其他程序有均攤多少memory, 即:

VSS- Virtual Set Size 虛擬耗用記憶體(包含共享庫佔用的記憶體)

RSS- Resident Set Size 實際使用物理記憶體(包含共享庫佔用的記憶體)

PSS- Proportional Set Size 實際使用的物理記憶體(比例分配共享庫佔用的記憶體)

USS- Unique Set Size 程序獨自佔用的物理記憶體(不包含共享庫佔用的記憶體)

(1). 記憶體的整體使用情況.

要分析memory leaks, 你需要知道總體的記憶體使用情況和劃分。 以判斷記憶體洩露是發生在user space, kernel space, mulit-media 等使用的memory, 從而進一步去判斷具體的memory leaks。

user space 使用的memory 即通常包括從程序直接申請的memory, 比如 malloc: 先mmap/sbrk 整體申請大塊Memory 後再malloc 細分使用, 比如stack memory, 直接透過mmap 從系統申請; 以及因user space 程序開啟檔案所使用的page cache, 以及使用ZRAM 壓縮 user space memory 儲存所佔用的memory。

kernel space 使用的memory 通常包括 kernel stack, slub, page table, vmalloc, shmem 等。

mulit-media 使用的memory 通常使用的方式包括 ion, gpu 等。

其他方式的memory 使用, 此類一般直接從buddy system 中申請出以page 為單位的memory, android 中比較常見如ashmem。

而從程序的角度來講, 通常情況下程序所使用的memory, 都會透過mmap 對映到程序空間後訪問使用(注: 也會一些非常特別異常的流程, 沒有mmap 到程序空間), 所以程序的memory maps 資訊是至關重要的。 對應在AEE DB 裡面的file 是 PROCESS_MAPS

下面列舉一些關鍵的段:

b1100000-b1180000 rw-p 00000000 00:00 0 [anon:libc_malloc]

malloc 透過jemalloc 所管控的空間, 常見的malloc leaks 都會可以看到這種libc_malloc段空間顯著增長。

address perms offset dev inode pathname

aefe5000-af9fc000 r-xp 00000000 103:0a 25039 /data/app/in。startv。hotstar-c_zk-AatlkkDg2B_FSQFuQ==/lib/arm/libAVEAndroid。so

af9fc000-afa3e000 r——p 00a16000 103:0a 25039 /data/app/in。startv。hotstar-c_zk-AatlkkDg2B_FSQFuQ==/lib/arm/libAVEAndroid。so

afa3e000-afad2000 rw-p 00a58000 103:0a 25039 /data/app/in。startv。hotstar-c_zk-AatlkkDg2B_FSQFuQ==/lib/arm/libAVEAndroid。so

第一段 “r-xp” 則是隻讀並可執行的主體程式碼段。 第二段 “r——p” 則是這個lib 使用的只讀變數段 , 第三段 “rw-p” 則是這個lib 使用的資料段。

7110f000-71110000 rw-p 00000000 00:00 0 [anon:。bss]

71712000-71713000 rw-p 00000000 00:00 0 [anon:。bss]

71a49000-71a4a000 rw-p 00000000 00:00 0 [anon:。bss]

BSS(Block Started by Symbol) 段, 存放程序未初始化的static 以及 gloal 變數, 預設初始化時全部為0。 通常此類不會有memory leaks, 基本上長度在程式啟動時就已經決定了。

//java thread

6f5b0b2000-6f5b0b3000 ——-p 00000000 00:00 0 [anon:thread stack guard]

6f5b0b3000-6f5b0b4000 ——-p 00000000 00:00 0

6f5b0b4000-6f5b1b0000 rw-p 00000000 00:00 0

//native thread

74d0d0e000-74d0d0f000 ——-p 00000000 00:00 0 [anon:thread stack guard]

74d0d0f000-74d0e0c000 rw-p 00000000 00:00 0

pthread stack 使用memory, 注意目前pthread create 時只標註了它底部的 “thread stack guard”, 預設pthread stack 大小是1M - 16K。 guard 是 4K。 注意的是java thread 在art 裡面還會再隔離一個page, 判斷收到的SIGSEGV 是否為StackOverflowError。

7e9cf16000-7e9cf17000 ——-p 00000000 00:00 0 [anon:thread signal stack guard]

7e9cf17000-7e9cf1b000 rw-p 00000000 00:00 0 [anon:thread signal stack]

對應Pthread signal stack, 大小為16K,同樣底部有guard 保護。

7f31245000-7f31246000 ——-p 00000000 00:00 0 [anon:bionic TLS guard]

7f31246000-7f31249000 rw-p 00000000 00:00 0 [anon:bionic TLS]

對應Pthread 的TLS, 長度為12K, 同樣底部有guard 保護。

edce5000-edce6000 rw-s 00000000 00:05 1510969 /dev/ashmem/shared_memory/443BA81EE7976CA437BCBFF7935200B2 (deleted)

此類是ashmem, 訪問/dev/ashmem 然後申請的memory, 通常比較關鍵是要確認它的name, 一般從它的name 可以明確得知memory 的申請位置。 至於 (deleted) 標識, 是指 mmap 時有帶MAP_FILE flag, 並且對應的path file已經unlink 或者不存在。

7e8d008000-7e8d306000 rw-s 00000000 00:0a 7438 anon_inode:dmabuf

7e8d306000-7e8d604000 rw-s 00000000 00:0a 7438 anon_inode:dmabuf

7e8d604000-7e8d902000 rw-s 00000000 00:0a 7438 anon_inode:dmabuf

7e8d902000-7e8dc00000 rw-s 00000000 00:0a 7438 anon_inode:dmabuf

ion memory 段。 ion buffer 的 vma name 標註成dmabuf, 即已經mmap 的ion memory 可以從這個直接統計算出。

注意的是, maps 列印的資訊只是地址空間資訊, 即是虛擬地址空間佔用情況, 而實際的具體的memory 佔用多少需要審查 proc/pid/smaps。 比如:

7e8ea00000-7e8ee00000 rw-p 00000000 00:00 0 [anon:libc_malloc]

Name: [anon:libc_malloc]

Size: 4096 kB

Rss: 888 kB

Pss: 888 kB

Shared_Clean: 0 kB

Shared_Dirty: 0 kB

Private_Clean: 0 kB

Private_Dirty: 888 kB

Referenced: 888 kB

Anonymous: 888 kB

AnonHugePages: 0 kB

ShmemPmdMapped: 0 kB

Shared_Hugetlb: 0 kB

Private_Hugetlb: 0 kB

Swap: 0 kB

SwapPss: 0 kB

KernelPageSize: 4 kB

MMUPageSize: 4 kB

Locked: 0 kB

VmFlags: rd wr mr mw me nr

比如這段jemalloc 使用的memory, 對應是一個4M 大小, 但實際目前使用的RSS=PSS=888K, 即還有大部分沒有真實填充memory。

同樣人為的檢視maps 比較耗時, 目前在android 裡面有 procrank, showmap, pmap 等命令可供檢視。 procrank 根據程序使用的memory 進行排序統計系統中程序的memory 使用量, 不過它一般沒有統計ion 等資訊。 注意的是這個命令預設只編譯到了debug 版本。

k71v1_64_bsp:/ # procrank -h

Usage: procrank [ -W ] [ -v | -r | -p | -u | -s | -h ]

-v Sort by VSS。

-r Sort by RSS。

-p Sort by PSS。

-u Sort by USS。

-s Sort by swap。

(Default sort order is PSS。)

-R Reverse sort order (default is descending)。

-c Only show cached (storage backed) pages

-C Only show non-cached (ram/swap backed) pages

-k Only show pages collapsed by KSM

-w Display statistics for working set only。

-W Reset working set of all processes。

-o Show and sort by oom score against lowmemorykiller thresholds。

-h Display this help screen。

showmap 根據程序的maps/smaps 進行統計排序, 注意的是這個命令預設只編譯到了debug 版本。

k71v1_64_bsp:/ # showmap

showmap [-t] [-v] [-c] [-q]

-t = terse (show only items with private pages)

-v = verbose (don‘t coalesce maps with the same name)

-a = addresses (show virtual memory map)

-q = quiet (don’t show error if map could not be read)

pmap 把maps 的每個段打印出來, 如果使用-x 則會使用smaps 中資料匹配, 統計PSS, SWAP 等。

OP46E7:/ # pmap ——help

usage: pmap [-xq] [pids。。。]

Reports the memory map of a process or processes。

-x Show the extended format

-q Do not display some header/footer lines

從系統角度來看memory 的使用情況, 通常會習慣性簡單的檢視 proc/meminfo: 下面簡單和大家分享具體的含義。

k71v1_64_bsp:/ # cat proc/meminfo

MemTotal: 3849612 kB

MemFree: 206920 kB

MemAvailable: 1836292 kB

Buffers: 73472 kB

Cached: 1571552 kB

SwapCached: 14740 kB

Active: 1165488 kB

Inactive: 865688 kB

Active(anon): 202140 kB

Inactive(anon): 195580 kB

Active(file): 963348 kB

Inactive(file): 670108 kB

Unevictable: 5772 kB

Mlocked: 5772 kB

SwapTotal: 1048572 kB

SwapFree: 787780 kB

Dirty: 32 kB

Writeback: 0 kB

AnonPages: 383924 kB

Mapped: 248488 kB

Shmem: 6488 kB

Slab: 391060 kB

SReclaimable: 199712 kB

SUnreclaim: 191348 kB

KernelStack: 22640 kB

PageTables: 28056 kB

NFS_Unstable: 0 kB

Bounce: 0 kB

WritebackTmp: 0 kB

CommitLimit: 2973376 kB

Committed_AS: 42758232 kB

VmallocTotal: 258867136 kB

VmallocUsed: 0 kB

VmallocChunk: 0 kB

CmaTotal: 2093056 kB

CmaFree: 78916 kB

我沿用kernel document 的註釋: /kernel/Documentation/filesystems/proc。txt

MemTotal: Total usable ram (i。e。 physical ram minus a few reserved

bits and the kernel binary code)

MemFree: The sum of LowFree+HighFree

MemAvailable: An estimate of how much memory is available for starting new

applications, without swapping。 Calculated from MemFree,

SReclaimable, the size of the file LRU lists, and the low

watermarks in each zone。

The estimate takes into account that the system needs some

page cache to function well, and that not all reclaimable

slab will be reclaimable, due to items being in use。 The

impact of those factors will vary from system to system。

Buffers: Relatively temporary storage for raw disk blocks

shouldn‘t get tremendously large (20MB or so)

Cached: in-memory cache for files read from the disk (the

pagecache)。 Doesn’t include SwapCached

SwapCached: Memory that once was swapped out, is swapped back in but

still also is in the swapfile (if memory is needed it

doesn‘t need to be swapped out AGAIN because it is already

in the swapfile。 This saves I/O)

Active: Memory that has been used more recently and usually not

reclaimed unless absolutely necessary。

Inactive: Memory which has been less recently used。 It is more

eligible to be reclaimed for other purposes

HighTotal:

HighFree: Highmem is all memory above ~860MB of physical memory

Highmem areas are for use by userspace programs, or

for the pagecache。 The kernel must use tricks to access

this memory, making it slower to access than lowmem。

LowTotal:

LowFree: Lowmem is memory which can be used for everything that

highmem can be used for, but it is also available for the

kernel’s use for its own data structures。 Among many

other things, it is where everything from the Slab is

allocated。 Bad things happen when you‘re out of lowmem。

SwapTotal: total amount of swap space available

SwapFree: Memory which has been evicted from RAM, and is temporarily

on the disk

Dirty: Memory which is waiting to get written back to the disk

Writeback: Memory which is actively being written back to the disk

AnonPages: Non-file backed pages mapped into userspace page tables

AnonHugePages: Non-file backed huge pages mapped into userspace page tables

Mapped: files which have been mmaped, such as libraries

Slab: in-kernel data structures cache

SReclaimable: Part of Slab, that might be reclaimed, such as caches

SUnreclaim: Part of Slab, that cannot be reclaimed on memory pressure

PageTables: amount of memory dedicated to the lowest level of page

tables。

NFS_Unstable: NFS pages sent to the server, but not yet committed to stable

storage

Bounce: Memory used for block device “bounce buffers”

WritebackTmp: Memory used by FUSE for temporary writeback buffers

CommitLimit: Based on the overcommit ratio (’vm。overcommit_ratio‘),

this is the total amount of memory currently available to

be allocated on the system。 This limit is only adhered to

if strict overcommit accounting is enabled (mode 2 in

’vm。overcommit_memory‘)。

The CommitLimit is calculated with the following formula:

CommitLimit = ([total RAM pages] - [total huge TLB pages]) *

overcommit_ratio / 100 + [total swap pages]

For example, on a system with 1G of physical RAM and 7G

of swap with a `vm。overcommit_ratio` of 30 it would

yield a CommitLimit of 7。3G。

For more details, see the memory overcommit documentation

in vm/overcommit-accounting。

Committed_AS: The amount of memory presently allocated on the system。

The committed memory is a sum of all of the memory which

has been allocated by processes, even if it has not been

“used” by them as of yet。 A process which malloc()’s 1G

of memory, but only touches 300M of it will show up as

using 1G。 This 1G is memory which has been “committed” to

by the VM and can be used at any time by the allocating

application。 With strict overcommit enabled on the system

(mode 2 in ‘vm。overcommit_memory’),allocations which would

exceed the CommitLimit (detailed above) will not be permitted。

This is useful if one needs to guarantee that processes will

not fail due to lack of memory once that memory has been

successfully allocated。

VmallocTotal: total size of vmalloc memory area

VmallocUsed: amount of vmalloc area which is used

VmallocChunk: largest contiguous block of vmalloc area which is free

我們可以得到一些大體的“等式”。

MemAvailable = free - kernel reserved memory + ative file + inactive file + SReclaimable - 2 * zone low water mark

Cached = All file page - buffers - swapping = Active file + Inactive file + Unevictable file - Buffers

Slab = SReclaimable + SUnreclaimable

Active = Active(anon) + Active(file)

Inactive = Inactive(anon) + Inactive(file)

AnonPages + Buffers + Cached = Active + Inactive

Buffers + Cached = Active(file) + Inactive(file)

SwapTotal = SwapFree + SwapUsed(Not SwapCached)

KernelStack = the number of kernel task * Stack Size(16K)

Kernel Memory Usage = KernelStack + Slab + PageTables + Shmem + Vmalloc

Native Memory Usage = Mapped + AnonPages + Others

(2). Android dumpsys meminfo 解析.

從Android 的角度, Google 提供了dumpsys meminfo 命令來獲取全域性以及某個程序的memory 資訊。 android 在 AativityManagerService 裡面提供了一個meminfo 的service , 可以來抓取process 的memory 使用概要, 這個慢慢成為了android 上層判斷的主流。

adb shell dumpsys meminfo ==> dump 全域性的memory 使用情況。 adb shell dumpsys meminfo pid ==> dump 單個process memory 使用情況。 它的一個好處在於, 如果是user build 沒有root 許可權的時候, 可以借道sh ==> system_server ==> binder ==> process 進行抓取操作, 規避了許可權方面的風險。

對應的完整操作命令:

OP46E7:/ # dumpsys meminfo -h

meminfo dump options: [-a] [-d] [-c] [-s] [——oom] [process]

-a: include all available information for each process。

-d: include dalvik details。

-c: dump in a compact machine-parseable representation。

-s: dump only summary of application memory usage。

-S: dump also SwapPss。

——oom: only show processes organized by oom adj。

——local: only collect details locally, don‘t call process。

——package: interpret process arg as package, dumping all

processes that have loaded that package。

——checkin: dump data for a checkin

——proto: dump data to proto

If [process] is specified it can be the name or

pid of a specific process to dump。

下面我們將解析dumpsys meminfo 的資料來源, 便於大家讀懂。

(2.1) 系統memory 來源解析.

Total RAM: 3,849,612K (status moderate)

Free RAM: 1,870,085K ( 74,389K cached pss + 1,599,904K cached kernel + 195,792K free)

Used RAM: 1,496,457K ( 969,513K used pss + 526,944K kernel)

Lost RAM: 686,331K

ZRAM: 48,332K physical used for 260,604K in swap (1,048,572K total swap)

Tuning: 384 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)

Total RAM: /proc/meminfo-MemTotal

Free RAM: cached pss = All pss of process oom_score_adj >= 900

cached kernel = /proc/meminfo。Buffers + /proc/meminfo。Cached + /proc/meminfo。SlabReclaimable- /proc/meminfo。Mapped

free = /proc/meminfo。MemFree

Used RAM: used pss: total pss - cached pss

Kernel: /proc/meminfo。Shmem + /proc/meminfo。SlabUnreclaim + VmallocUsed + /proc/meminfo。PageTables + /proc/meminfo。KernelStack

Lost RAM: /proc/meminfo。memtotal - (totalPss - totalSwapPss) - /proc/meminfo。memfree - /proc/meminfo。cached - kernel used - zram used

(2.2) 單個Process 資料來源解析

單個process 則透過binder 接入app 來抓取。 接到ActivityThread 的 dumpMeminfo 來統計。

Native Heap, 從jemalloc 取出,對應實現是 android_os_Debug_getNativeHeapSize() ==> mallinfo() ==> jemalloc

Dalvik Heap, 使用Runtime 從java heap 取出。

同時根據process 的smaps 解析資料 Pss Private Dirty Private Clean SwapPss。

** MEMINFO in pid 1138 [system] **

Pss Private Private SwapPss Heap Heap Heap

Total Dirty Clean Dirty Size Alloc Free

———— ———— ———— ———— ———— ———— ————

Native Heap 62318 62256 0 0 137216 62748 74467

Dalvik Heap 21549 21512 0 0 28644 16356 12288

Dalvik Other 4387 4384 0 0

Stack 84 84 0 0

Ashmem 914 884 0 0

Other dev 105 0 56 0

。so mmap 10995 1112 4576 0

。apk mmap 3912 0 2776 0

。ttf mmap 20 0 0 0

。dex mmap 60297 76 57824 0

。oat mmap 2257 0 88 0

。art mmap 3220 2788 12 0

Other mmap 1944 4 672 0

GL mtrack 5338 5338 0 0

Unknown 3606 3604 0 0

TOTAL 180946 102042 66004 0 165860 79104 86755

App Summary

Pss(KB)

————

Java Heap: 24312

Native Heap: 62256

Code: 66452

Stack: 84

Graphics: 5338

Private Other: 9604

System: 12900

TOTAL: 180946 TOTAL SWAP PSS: 0

Objects

Views: 11 ViewRootImpl: 2

AppContexts: 20 Activities: 0

Assets: 15 AssetManagers: 0

Local Binders: 528 Proxy Binders: 1134

Parcel memory: 351 Parcel count: 370

Death Recipients: 627 OpenSSL Sockets: 0

WebViews: 0

SQL

MEMORY_USED: 384

PAGECACHE_OVERFLOW: 86 MALLOC_SIZE: 117

DATABASES

pgsz dbsz Lookaside(b) cache Dbname

4 64 85 12/29/8 /data/system_de/0/accounts_de。db

4 40 0/0/0 (attached) ceDb: /data/system_ce/0/accounts_ce。db

4 20 27 54/17/3 /data/system/notification_log。db

我給大家解釋一下:

Java Heap: 24312 dalvik heap + 。art mmap

Native Heap: 62256

Code: 66452 。so mmap + 。jar mmap + 。apk mmap + 。ttf mmap + 。dex mmap + 。oat mmap

Stack: 84

Graphics: 5338 Gfx dev + EGL mtrack + GL mtrack

Private Other: 9604 TotalPrivateClean + TotalPrivateDirty - java - native - code - stack - graphics

System: 12900 TotalPss - TotalPrivateClean - TotalPrivateDirty

下面的解釋來源於

https://

developer。android。com/s

tudio/profile/investigate-ram?hl=zh-cn

Dalvik Heap 您的應用中 Dalvik 分配佔用的 RAM。Pss Total 包括所有 Zygote 分配(如上述 PSS 定義所述,透過程序之間的共享記憶體量來衡量)。Private Dirty 數值是僅分配到您應用的堆的實際 RAM,由您自己的分配和任何 Zygote 分配頁組成,這些分配頁自從 Zygote 派生應用程序以來已被修改。

Heap Alloc 是 Dalvik 和原生堆分配器為您的應用跟蹤的記憶體量。此值大於 Pss Total 和 Private Dirty,因為您的程序從 Zygote 派生,且包含您的程序與所有其他程序共享的分配。

。so mmap 和 。dex mmap 對映的 。so(原生)和 。dex(Dalvik 或 ART)程式碼佔用的 RAM。Pss Total 數值包括應用之間共享的平臺程式碼;Private Clean 是您的應用自己的程式碼。通常情況下,實際對映的記憶體更大 - 此處的 RAM 僅為應用執行的程式碼當前所需的 RAM。不過,。so mmap 具有較大的私有髒 RAM,因為在載入到其最終地址時對原生程式碼進行了修改。

。oat mmap 這是程式碼映像佔用的 RAM 量,根據多個應用通常使用的預載入類計算。此映像在所有應用之間共享,不受特定應用影響。

。art mmap 這是堆映像佔用的 RAM 量,根據多個應用通常使用的預載入類計算。此映像在所有應用之間共享,不受特定應用影響。儘管 ART 映像包含 Object 例項,它仍然不會計入您的堆大小。

(3). 記憶體使用情況監測

我們說通常的監測機制有兩種。 一種是輪詢, 週期性的檢視memory 的使用情況, 通常是透過指令碼或者daemon 程式週期性的監測。 監測的資料一般包括: /proc/meminfo 系統總的memory 使用情況。 /proc/zoneinfo 每個zone 的memory 使用情況。 /proc/buddyinfo buddy system 的memory 情況。 /proc/slabinfo slub 的memory 使用分佈。 /proc/vmallocinfo vmalloc 的memory 使用情況。 /proc/zraminfo zram 的使用情況, 以及佔用memory 情況。 /proc/mtk_memcfg/slabtrace slab memory 的具體分佈。 /proc/vmstat 系統memory 根據使用型別的分佈。 /sys/kernel/debug/ion/ion_mm_heap mtk multi-media ion memory 使用情況。 /sys/kernel/debug/ion/client_history ion 各個clients 使用的ion 情況粗略統計。 /proc/mali/memory_usage arm mali gpu 使用memory 按程序統計 /sys/kernel/debug/mali0/gpu_memory arm mali gpu 使用memory 按程序統計 ps -A -T 列印系統所有程序/執行緒資訊, 可觀察每個程序的執行緒量, 以及VSS/RSS dumpsys meminfo 從Android 角度觀察系統memory 的使用情況。 /sys/kernel/debug/mlog mtk 統計系統一段時間(約60s) 系統memory的使用情況, 包括kernel, user space, ion, gpu 等的分佈。

大家可以寫腳本週期性的抓取。 這裡單獨把mlog 抓出來說明, mlog 是MTK 開發的輕量級的memory log, 一體式抓取常見的memory 統計資訊, 包括kernel(vmalloc, slub。。。), user space (程序VSS,RSS。。。), ion, gpu 等在一段時間內部的概要使用情況。 並且提供了圖形化的tool 來展示具體的memory 分佈, 使用情況, 非常方便, 請大家優先使用(tool_for_memory_analysis)。 下面是一些精美的截圖:

各類memory 在一段時間內的變化情況:

Linux記憶體使用情況與監測

Linux記憶體使用情況與監測

Kernel/User/HW 的memory 統計變化:

Linux記憶體使用情況與監測

一段時間內, 抓取的程序的memory 變化情況:

歡迎大家手工使用。

另外一種熔斷, 即限制memory 的使用, 當到一定程度時, 主動發生異常, 回報錯誤。 通常情況下, 系統memory leaks , 就會伴隨OOM 發生, 嚴重是直接KE。 而單個程序 memory leaks, 如果它的oom adj < 0, 即daemon service 或者 persist app, 通常它的memory leaks 也會觸發系統OOM , 因為lmk 難以殺掉。 如果是普通app 發生memory leaks, 則往往直接被LMK 殺掉。 難以對系統產生直接異常。 當然程序也可能無法申請到memory 發生JE, NE 等異常。

針對總的系統的memory 使用, 我們可以透過設定, 限制系統總體的memory, 比如設定系統最大2GB: (1)。 ProjectConfig。mk CUSTOM_CONFIG_MAX_DRAM_SIZE = 0x80000000 注意: CUSTOM_CONFIG_MAX_DRAM_SIZE must be included by AUTO_ADD_GLOBAL_DEFINE_BY_NAME_VALUE

(2)。 preloader project config file vendor/mediatek/proprietary/bootable/bootloader/preloader/custom/{project}/{project}。mk CUSTOM_CONFIG_MAX_DRAM_SIZE = 0x80000000 注意: CUSTOM_CONFIG_MAX_DRAM_SIZE must be exported

針對某個程序使用的memory, 我們可以透過setrlimit 來進行限制, 如: 針對camerahalserver, 使用init 的setrlimit 進行限制。

service camerahalserver /vendor/bin/hw/camerahalserver

class main

user cameraserver

group audio camera input drmrpc sdcard_rw system media graphics

ioprio rt 4

capabilities SYS_NICE

writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks

#limit VSS to 4GB

rlimit as 0x100000000 0x100000000

#limit malloc to 1GB

rlimit data 0x40000000 0x40000000

把camerahalserver 的VSS 限制到4GB, 把malloc 的大小限制到1GB, 一旦超出就會返回 ENOMEM, 通常情況下,這樣可自動產生NE。 以便抓到camerahalserver 的更多資訊。

注意的是因為vendor 下面的service 是由 vendor_init 拉起來的, 需要給vendor_init 設定sepolicy。 以免無法設定成功。

/device/mediatek/sepolicy/basic/non_plat/vendor_init。te

allow vendor_init self:global_capability_class_set sys_resource;

也可以直接在程式碼裡面寫死, 參考如/frameworks/av/media/libmedia/MediaUtils。cpp

針對APP java heap的memory leaks, 我們可以透過設定 dalvik 的heap size 進行限制, 透過system property 設定, 注意的是, 目前的做法會影響到所有的java process。

[dalvik。vm。heapgrowthlimit]: [384m]

[dalvik。vm。heapsize]: [512m]