目录
1 基本概念
1.1 PSS & RSS & USS & VSS
1.1.1 PSS
1.1.2 RSS
1.2 Dirty & Clean & SwapPss
1.2.1 Private Dirty
1.2.2 Private Clean
1.2.3 SwapPss Dirty
1.3 Swap & buffers & cache
1.3.1 Swap
1.3.2 buffers
1.3.3 cache
2、命令解读
2.1 dumpsys meminfo
2.2 dumpsys meminfo packagename
2.3 adb shell free
2.4 adb shell top
2.5 cat /proc/meminfo
2.6 cat proc/dma_heap/all_heaps
3、LWKD
3.1 oom_odj
3.2 killinfo
3.2.1 转换excl表格
3.2.2 各字段的意义
4、PSI
4.1 ANR的PSI值
4.2 LWKD的PSI值
参考Android内存分析命令_dumpsys meminfo 算出rss-CSDN博客
1 基本概念
1.1 PSS & RSS & USS & VSS
1.1.1 PSS
- 概念:全称Proportional Set Size,根据进程实际使用的内存量按照共享比例分配给进程的一种内存度量方式。如果多个进程共享同一块内存页,那么共享的内存会被均摊到各个进程的 Pss 中,以更准确地反映各个进程实际使用的内存。
- 意义:根据共享比例分配内存的方式,因此相比于简单的内存统计,它提供了更精确的内存使用情况,避免重复计算共享内存,更好地反映进程真实的内存占用情况。
1.1.2 RSS
- 概念:全称Resident Set Size,指示一个进程当前在物理内存中占用的实际大小,包括私有页和共享页的总和。这表示了一个进程实际占用了多少物理内存资源。
- 意义:更加直接地表示了一个进程当前占用的物理内存大小,包括私有页和共享页。通过监控 Rss,可以了解一个进程实际消耗的系统资源,帮助识别内存泄漏、优化内存使用等问题。
1.2 Dirty & Clean & SwapPss
1.2.1 Private Dirty
- 概念:表示进程私有页中已经修改(脏数据)的内存量。这些数据是进程独占并且已被修改的,通常表示进程对这部分内存执行了写入操作
- 意义:Private Dirty占比过高,可能意味着进程频繁地修改内存数据,导致系统产生大量的脏页,增加了写入操作的开销。优化这部分内存的管理,减少不必要的写入操作,可以提高系统性能
1.2.2 Private Clean
- 概念:表示进程私有页中未被修改(干净数据)的内存量。这些数据是进程独占但未被修改的,通常表示这部分内存没有发生写入操作。
- 意义:Private Clean 有助于了解进程对内存的读取操作情况和内存使用效率。较高的 Private Clean 可能暗示着进程大量读取数据但很少进行写入操作,即访问模式偏向读取而不是写入。
1.2.3 SwapPss Dirty
- 概念:用于监视系统中内存交换操作的情况。当系统内存不足时,操作系统会将部分内存页交换到磁盘的交换空间以腾出物理内存供其他程序使用。
- 意义:SwapPss 提供了一个指标,用于监视系统中内存交换操作的情况。当系统内存不足时,操作系统会将部分内存页交换到磁盘的交换空间以腾出物理内存供其他程序使用。高 SwapPss 值通常暗示着系统内存不足,导致操作系统频繁地将内存页交换到磁盘上的交换空间,这可能会对系统性能产生负面影响。
- 作用:此值通常出现在dumpsys meminfo packagename命令里面
1.3 Swap & buffers & cache
1.3.1 Swap
- 概念:是一种操作系统用来缓解物理内存不足的机制。当系统的物理内存达到极限,而仍有进程需要更多内存时,操作系统会将不常用的内存页面移动到硬盘上的交换空间(Swap),以释放物理内存给那些需要更多内存的进程使用。交换空间通常位于硬盘上,并作为虚拟内存来扩展系统的内存容量。尽管交换空间能够帮助系统继续正常运行,但由于硬盘访问速度远慢于内存,因此使用交换空间会导致系统性能下降。因此,最好的情况是尽量避免频繁地使用交换空间,以确保系统能够在物理内存中运行。
- 作用:Swap实际上是一段占用在磁盘上的空间,他的主要用途如下:在物理内存RAM不足的时候操作系统将一些不常用的内存页从物理RAM移动到SWAP空间里面,这样将腾出更多的RAM空间运行其他活跃的进程;可以通过SWAP作为虚拟内存来扩展系统的内存容量,例如一些手机厂商定制的内存融合方案。
- 意义:如果swap的使用率比较高,说明当前系统物理内存RAM不足,个人理解这只是一个参考指标,因为在内存ram充足的时候也存在swap的使用,在扩展系统内存的方案里面也存在使用swap,因此通常需要发现问题的时候进行对比参考。
1.3.2 buffers
- 概念:用于临时存储数据的内存区域。当系统需要将数据从一个地方传输到另一个地方时,会使用缓冲区来暂时存储这些数据,以提高数据传输的效率。缓冲区通常用于处理I/O操作,比如磁盘读写、网络数据传输等。数据先被写入缓冲区,然后再由系统异步地将数据传输到目标位置,避免了频繁访问实际设备所带来的性能开销。
- 意义:buffers被分配在RAM中,主要针对IO/网络等场景下。
1.3.3 cache
- 概念:这里主要指的swap cache,指系统将交换空间(Swap)中的数据缓存到内存中,以便快速访问已经移至交换空间的数据。当系统发现某些数据频繁地被交换到交换空间中(因为物理内存不足等原因),为了提高这些数据的访问速度,系统会将这些数据缓存到内存中,这样下次访问这些数据时就可以更快地获取,而不必每次都从交换空间重新读取。
- 意义:swap cache同buffers一样,他也被分配在RAM中的一段缓存区,但不同的是swap cache里面的缓存数据是专门针对需要和swap交换的内存数据,而buffer里面的缓存数据可能被用于I/O读写/网络数据传输等其他各种场景(PS:个人理解)。
2、命令解读
2.1 dumpsys meminfo
此命令查询系统当前整体的内存状态,如下先打印了当前的时间搓
C:\Users\Administrator>adb shell dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 32351135 Realtime: 85587685
-
Total RSS by Process:展示了系统当前各个进程的RSS使用情况,按照顺序排列
Total RSS by process:
1,203,384K: io.supercent.pizzaidle (pid 4810 / activities)------> PS:此时的前台进程RSS占用1203M
250,004K: system (pid 1445)
160,036K: com.google.android.gms (pid 22458)
158,204K: com.android.systemui (pid 1804)
130,548K: surfaceflinger (pid 786)
127,192K: camerahalserver (pid 1076)
88,844K: com.google.android.gms.persistent (pid 16497)
............
-
Total RSS by OOM adjustment:按照OOM调整的类别来进行展示
Total RSS by OOM adjustment:
848,928K: Native --->下面是native层的进程(包括系统和底层服务),总计848M
130,548K: surfaceflinger (pid 786)
127,192K: camerahalserver (pid 1076)
77,560K: android.hardware.graphics.composer@3.1-service (pid 729)
23,808K: zygote64 (pid 716)
14,396K: android.hardware.media.c2@1.2-mediatek-64b (pid 731)
.........250,004K: System --->下面是系统进程(如system进程,注意systemui不算),总计250M
250,004K: system (pid 1445)
339,500K: Persistent -->持久性进程(如systemui)感觉像是配置了Persistent参数的进程
158,204K: com.android.systemui (pid 1804)
47,072K: com.android.phone (pid 2060)
40,340K: com.android.networkstack.process (pid 1925)
29,560K: com.mediatek.ims (pid 2025)72,824K: Persistent Service -->持久性服务进程
37,052K: com.android.bluetooth (pid 21973)
35,772K: com.google.android.providers.media.module (pid 3149)
1,423,652K: Foreground --->前台进程(通常是用户当前与之交互的应用程序)
1,203,384K: io.supercent.pizzaidle (pid 4810 / activities) --->当前正在玩这个游戏
88,844K: com.google.android.gms.persistent (pid 16497)
71,740K: com.google.android.webview:sandboxed_process0:org.chromium.content.app.SandboxedProcessService0:0 (pid 5275)
59,684K: com.google.android.adservices.api (pid 3890)
962,372K: Visible --->可见进程(但是并没有操作的进程)
160,036K: com.google.android.gms (pid 22458)
194,036K: Perceptible --->这些进程是可察觉的,即用户可以感知到其活动或影响
54,924K: com.google.android.inputmethod.latin (pid 17551)
38,876K: com.google.android.apps.messaging:rcs (pid 16560)
58,364K: Perceptible Medium -->这些进程也是可察觉的,但相对于 Perceptible 类别来说,它们的活动可能更为中等或轻微
58,364K: com.google.android.googlequicksearchbox:search (pid 3543)
46,896K: Backup -->备份进程,在某些情况下可能会执行备份操作
46,896K: com.dpc.plat.appupdate (pid 3056)
43,308K: Previous -->之前的进程,可能指在过去某个时间段内运行过的进程
43,308K: com.google.process.gservices (pid 32449)
181,300K: Cached -->缓存进程,一般用于存储最近使用过的数据或资源以提高访问速度
79,588K: com.hoffnung:remote (pid 6009)
51,536K: com.google.android.permissioncontroller (pid 5059)
- Total RSS by category:按照类别(非进程的角度)来进行展示
Total RSS by category:
1,086,084K: .so mmap ->表示共有 1086M的内存用于动态链接库的内存映射操作
580,804K: GL mtrack ->代表图形库OpenGL内存追踪所占用的内存大小为 580M
466,792K: .art mmap ->表示用于 Android 运行时环境(ART)的内存映射占用
354,016K: EGL mtrack ->指 OpenGL ES 内存追踪占用了 354,016K 的内存量
351,796K: .jar mmap ->表示 Java 程序文件(.jar 文件)的内存映射使用量
313,896K: .oat mmap ->用于优化程序的可执行文件和数据缓存.oat 文件的内存映射
226,636K: Native -->表示本地代码进程所占用的内存
212,288K: Unknown ->未知类型的资源占用
187,164K: Dalvik -->Dalvik 虚拟机相关的资源消耗
181,812K: .apk mmap ->APK 文件的内存映射占用
148,348K: .dex mmap ->可执行文件.dex 文件的内存映射占用
130,448K: Other mmap ->其他类型的内存映射占用
86,284K: Dalvik Other ->Dalvik 虚拟机的其他资源消耗了 86,284K 的内存
70,496K: Other dev ->其他开发相关的资源占用
19,296K: Stack ->栈内存占用了 19,296K 的空间
4,844K: Ashmem ->匿名共享内存(Ashmem)占用了 4,844K 的内存
180K: .ttf mmap ->TrueType 字体文件的内存映射占用了 180K 的内存
0K: Cursor ->游标相关的资源没有占用任何内存
0K: Gfx dev ->图形设备相关的资源也没有占用内存
0K: Other mtrack ->其他内存跟踪资源没有占用内存
- Total PSS by process:同上
- Total PSS by OOM adjustment:同上
- Total PSS by category:同上
- 整体使用情况
Total RAM: 3,697,392K (status normal) ->总共的RAM容量为3697M,这是一台4G手机
Free RAM: 548,930K ( 49,482K cached pss + 448,860K cached kernel + 50,588K free) ->空闲的RAM为 548M(其中包括49M用于缓存 PSS,448M 用于缓存内核,还有 50M是完全空闲的)
DMA-BUF: 119,856K ( 14,080K mapped + 105,776K unmapped) ->表示系统中DMA-BUF的总大小为 119M,它是直接内存访问缓冲区,即不通过cpu将内存映射给用户空间的进程或者硬件设备,其中14M已经映射了,105M还未被映射
DMA-BUF Heaps: 119,808K
DMA-BUF Heaps pool: 75,540K
GPU: 679,348K ( 114,628K dmabuf + 564,720K private) ->GPU占用了679M的内存,其中114M是 DMA-BUF映射而来,564M是GPU独有的
Used RAM: 4,001,150K (2,718,430K used pss + 1,282,720K kernel) ->使用的内存4001M,包括2718M的PSS和1282M的内核内存,这里有个疑问为什么会比总RAM多?
Lost RAM: 182,646K -->丢失的内存
ZRAM: 468,452K physical used for 1,541,632K in swap (2,773,040K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
疑问1:为什么Used RAM高于Total RAM?
疑问2:DMA-BUF到底是什么?
疑问3:为什么会存在丢失内存?
案例1:DLS占用大量内存
DLS在4G手机启动之前:
Total RSS by process:
266,232K: system (pid 1478)
221,832K: com.google.android.googlequicksearchbox:search (pid 4938)
215,468K: com.android.systemui (pid 20592)
173,252K: com.google.android.apps.messaging (pid 5229)
166,476K: com.google.android.inputmethod.latin (pid 4344)
162,376K: com.google.android.apps.tachyon (pid 5275)
149,084K: com.android.settings (pid 4849)Total RAM: 3,697,392K (status normal)
Free RAM: 1,538,968K ( 569,136K cached pss + 788,280K cached kernel + 181,552K free)
DMA-BUF: 132,776K ( 0K mapped + 132,776K unmapped)
DMA-BUF Heaps: 132,728K
DMA-BUF Heaps pool: 42,672K
GPU: 142,884K ( 99,720K dmabuf + 43,164K private)
Used RAM: 2,352,571K (1,612,199K used pss + 740,372K kernel)
Lost RAM: 270,812K
ZRAM: 241,944K physical used for 815,344K in swap (2,773,040K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
DLS在4G手机启动之后:
Total RSS by process:
1,884,668K: com.firsttouchgames.dls7 (pid 6643 / activities)
158,388K: surfaceflinger (pid 799)
121,768K: android.hardware.graphics.composer@3.1-service (pid 753)
101,680K: system (pid 1478)Total PSS by process:
2,087,176K: com.firsttouchgames.dls7 (pid 6643 / activities)
225,445K: system (pid 1478)
185,186K: surfaceflinger (pid 799)
123,892K: android.hardware.graphics.composer@3.1-service (pid 753)Total RAM: 3,697,392K (status normal)
Free RAM: 205,508K (0K cached pss + 168,960K cached kernel + 36,548K free)
DMA-BUF: 170,304K (48K mapped + 170,256K unmapped)
DMA-BUF Heaps: 170,256K
DMA-BUF Heaps pool: 0K
GPU: 1,918,964K ( 152,280K dmabuf + 1,766,684K private)
Used RAM: 4,117,655K (1,632,495K used pss + 2,485,160K kernel)
Lost RAM: 281,699K
ZRAM: 423,156K physical used for 1,381,628K in swap (2,773,040K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
2.2 dumpsys meminfo packagename
此命令可以带上包名,查询指定进程的内存详细使用情况,阅读之前先理解《Dirty & Clean & SwapPss》相关概念
C:\Users\Administrator>adb shell dumpsys meminfo io.supercent.pizzaidle
- MEMINFO & App Summary:单位K,反映当前进程两种不同分类的部分占用情况
-
Objects & SQL
2.3 adb shell free
-
Mem total:真实物理内存RAM的总空间,如上是一台4G内存手机的Mem total
-
Mem used:已经使用的物理内存大小,包括一些缓存内存大小
-
Mem free:空闲的物理内存,他和Mem used之和等于Mem total
-
Mem shared:被多个进程共享的内存大小,例如某个共享so可能被多个进程引用
-
Mem buffers:详细参考《swap & buffers & cache》,为了提升类似IO等传输效率,在RAM开辟的一段缓存区域
-
-/+ buffers/cache:其used表示系统实际使用的内存大小(不包含缓存内存大小),主要区别Mem used
-
Swap Total:磁盘上Swap交换空间总容量
-
Swap used:磁盘上Swap交换空间使用的容量,即代表有多少内存数据存储了进来
-
Swap free:磁盘上Swap交换空间未使用的容量。
理解1:Mem total = Mem used + Mem free;可用物理RAM的总大小,可以分为已经使用的Mem used部分,和没有使用的部分。其中Mem used包括实际进程真实使用的,也包括Mem buffers缓存使用的部分。
理解2:Mem total = buffers/cache used + buffers/cache free;可用物理RAM的总大小,可用分为实际使用的buffers/cache used,和空闲的部分。PS:buffers/cache used代表的大小是去除了mem buffers或者cache相关的, 它与Mem used不同真实反映了各个进程真实使用的大小。
理解3:Mem used = buffers/cache used + Mem buffers;Mem used的计算包括了实际使用的和缓存里面的大小。通过Mem used和buffers/cache used的差异可以看出当前缓存空间的使用情况。
理解4:Swap total = Swap used + Swap free;磁盘Swap空间总大小,可以分为已经使用的used部分,和没有使用的部分。它位于磁盘swap,它的大小可以通过配置来指定,往往和已经固定死的RAM没有什么关联,swap used使用率比较高反映当前可用RAM空间不足。
2.4 adb shell top
这里暂时只针对top命令的内存相关部分进行介绍,如上图,其展示的指标其实和free基本上一致
Mem total = Mem used + Mem free
Swap total = Swap used + Swap free
PS:在同一台机器同一时刻执行top和free命令,他们之间存在一些偏差,但整体来看是差不多的,个人估计可能是内部使用了不同算法来统计或者根本无法做到同一时刻执行,所以建议这点偏差可以忽略。
2.5 cat /proc/meminfo
如下命令输出,其结果跟adb shell top比较接近
C:\Users\Administrator>adb shell cat /proc/meminfo
MemTotal: 3697392 kB
MemFree: 83888 kB
MemAvailable: 1368764 kB
Buffers: 2164 kB
Cached: 1308024 kB
SwapCached: 144664 kB
Active: 855264 kB
Inactive: 1243924 kB
Active(anon): 341388 kB
Inactive(anon): 552064 kB
Active(file): 513876 kB ^
Inactive(file): 691860 kB
Unevictable: 90340 kB
Mlocked: 90340 kB
SwapTotal: 2773040 kB
SwapFree: 1819848 kB
Dirty: 52 kB
Writeback: 0 kB
AnonPages: 860336 kB
Mapped: 707324 kB
Shmem: 20412 kB
KReclaimable: 175176 kB
Slab: 400312 kB
SReclaimable: 139980 kB
SUnreclaim: 260332 kB
KernelStack: 57760 kB
ShadowCallStack: 14476 kB
PageTables: 101824 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 4621736 kB
Committed_AS: 69377560 kB
VmallocTotal: 259653632 kB
VmallocUsed: 226572 kB
VmallocChunk: 0 kB
Percpu: 77184 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 409600 kB
CmaFree: 5252 kB
- MemTotal:所有可用RAM内存大小,即物理内存容量减去一些预留位(所以整体比实际物理内存容量小)。系统从加电开始到引导完成,引导程序、内核等需要保留一些内存空间,最后剩下的MemTotal,他在系统运行期间保持恒定不变的。
- MemFree:没有使用的空闲内存总容量
- MemAvailable:可以被应用程序使用的内容总容量。系统中虽然有些内存已经被使用了,但是可以进行回收,例如cache/buffers、slab等,所以MemFree并不能代表当前还能够提供多少内存空间给应用程序。即MemAvailable约等于MemFree + buffers + cached,它是内核使用的特定算法估算出来的一个值。
参考cat /proc/meminfo 各字段详解_cat proc meminfo-CSDN博客
参考Linux的缓存内存(cache memory)_linux cache-CSDN博客
2.6 cat proc/dma_heap/all_heaps
此命令用来输出dma_heap相关的详细信息,如下命令输出结果:
- pid table:一些进程的PID和进程名称
- inode:显示了一些 inode(索引节点)的详细信息,包括大小、缓冲计数、分配进程
疑问1:DMA-HEAP到底是什么?
疑问2:DMA-HEAP到底可以干什么?
3、LWKD
LWKD全称lowmemorykiller。之前的Android使用内核中的低内存终止守护程序 (LMK) 驱动程序来监控系统内存压力,该驱动程序是一种依赖于硬编码值的严格机制。从内核 4.12 开始,LMK 驱动程序已从上游内核中移除,改由用户空间 lmkd 来执行内存监控和进程终止任务。低内存终止守护程序 (lmkd) 进程可监控运行中的 Android 系统的内存状态,并通过终止最不必要的进程来应对内存压力大的问题,使系统以可接受的性能水平运行。
其工作原理可以参考Low Memory Killer(一) - android源码分析 - 简书
如上经典日志,lowmemorykiller非常活跃,进行疯狂查杀优先级低的进程,其中可以看到圈红的地方:
- adj为0的进程都已经开始被查杀了,说明当前内存的压力已经很大了(lwkd通过adj给android所有进程进行分类,从优先级最低的空进程/缓存进程到优先级很高的前台进程和系统核心进程,adj依次减小,详情可以参考Android系统为什么lmkd杀到adj 100就代表有低内存?_lmkd adj-CSDN博客
- reason为被kill的原因,如果是low watermark通常为内存压力超过了配置的内存水位线,这个时候也表示内存压力大,当然我们可以动态调节这个水位线,并不是越高越好也不是越低越好,这个值通常需要多方测试取一个平衡的值。
- 通常可以认为lwkd查杀的进程adj在900左右,说明当前系统内存状态还好,被杀的都是些缓存进程,如果被杀的进程adj在200以下,就说明当前系统内存已经严重不足了,因为这些进程已经被用户能感知的了
3.1 oom_odj
adb shell dumpsys meminfo
adb shell dumpsys activity lru
adb shell dumpsys activity processes
cat /sys/module/lowmemorykiller/parameters/minfree //文件中每一个数字代表一个内存级别
cat /sys/module/lowmemorykiller/parameters/adj //文件中每一个数字代表一个进程优先级级别
3.2 killinfo
lwkd在system/memory/lmkd/event.logtags文件中定义了一些event事件,此文件可能存在多份,一定要查清楚当前项目使用的是那一份,定义格式如下:
# The entries in this file map a sparse set of log tag numbers to tag names.
# This is installed on the device, in /system/etc, and parsed by logcat.
#
# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
# negative values alone for now.)
#
# Tag names are one or more ASCII letters and numbers or underscores, i.e.
# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
# impacts log readability, the latter makes regex searches more annoying).
#
# Tag numbers and names are separated by whitespace. Blank lines and lines
# starting with '#' are ignored.
#
# Optionally, after the tag names can be put a description for the value(s)
# of the tag. Description are in the format
# (<name>|data type[|data unit])
# Multiple values are separated by commas.
#
# The data type is a number from the following values:
# 1: int32_t
# 2: int64_t
# 3: string
# 4: list
# 5: float
#
# The data unit is a number taken from the following list:
# 1: Number of objects
# 2: Number of bytes
# 3: Number of milliseconds
# 4: Number of allocations
# 5: Id
# 6: Percent
# s: Number of seconds (monotonic time)
# Default value for data of type int/long is 2 (bytes).
#
# TODO: generate ".java" and ".h" files with integer constants from this file.# for killinfo logs
10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemTotal|1),(MemFree|1),(MemAvailable|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(Mlocked|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1),(MsSinceEvent|1),(MsSincePrevWakeup|1),(WakeupsSinceEvent|1),(SkippedWakeups|1),(TaskSwapSize|1),(GPU|1),(Thrashing|1),(MaxThrashing|1),(PsiMemSome|5),(PsiMemFull|5),(PsiIoSome|5),(PsiIoFull|5),(PsiCpuSome|5)
3.2.1 转换excl表格
上文不是很好看,可用如下几个步骤将其转换为表格:
步骤一:提取event日志中的killinfo信息
grep --color -iE "killinfo" logcat_events_2024_0904_1811.log > killinfo.txt
步骤二:使用Notepad++直接进行替换分隔符为\t
步骤三:将其复制到表格中,并将event.logtags中的信息作为标头,最后形成如下表格
3.2.2 各字段的意义
4、PSI
PSI全称Pressure Stall Information,提供了一种评估系统资源压力的方法,它提供了一种实时检测系统资源竞争程度的方法,以竞争等待时间的方式呈现。简单的可以理解为“任务因资源短缺而延时的时间占比”。详情参考
Getting Started with PSI · PSI
full:表示在某段时间内,所有进程或者任务等待某资源耗时占比
some:表示某段时间内,单个进程或者任务等待某资源耗时最长的占比
其中某资源通常有三类资源:IO、memory、CPU,其中CPU不可能存在full,因为无论任何时刻CPU总在执行某个任务,否则该系统已经宕机
注意:他们的值表示单位时间段内的百分比,例如假设full为10.0,单位时间长度为10秒,那么表示所有任务等待某资源耗时了1秒,可用抽线的理解为系统此资源短缺阻塞了1秒的时长。
4.1 ANR的PSI值
如上avg10的时候memory和IO的Some PSI值达到了90%以上,Full PSI值达到了40%以上,表示有进程等待memory/IO资源超过9秒,有4秒的时间是所有进程都等待不了memory/IO资源,表示当前系统负载比较高
4.2 LWKD的PSI值
通常可以认为LWKD里面的PSI超过10,此时系统资源压力比较高,但同时也需要结合killinfo其他指标综合分析。
如下一份killinfo转换之后,可以根据事件判断swap越来越低,但是内存压力并不大,SUnreclaim占用比较高,说明可能存在kernel内存泄漏
疑问1:为什么swap剩余空间低但是内存压力并不大?
疑问2:为什么SUnreclaim指标指向内存泄漏?
5、Kswapd
内核交换守护程序 (kswapd) 是 Linux 内核的一部分,用于将已使用内存转换为可用内存。当设备上的可用内存不足时,该守护程序将变为活动状态。Linux 内核设有可用内存上下限阈值。当可用内存降至下限阈值以下时,kswapd 开始回收内存。当可用内存达到上限阈值时,kswapd 停止回收内存。
重点:kswapd与前文提到的lwkd不是同一个东西。lwkd在android新的版本从Linux内核的一个进程移动到了Framework层,它的主要功能是通过kill进程来缓解系统内存压力;而kswapd从始至终都是Linux内核的一个进程,通过内存的回收来缓解系统内存压力。
工作原理参考:kswapd进程工作原理(一)——初始化及触发-CSDN博客
源码逻辑参考:kswapd介绍_算法_内核工匠-开放原子开发者工作坊
5.1 kswapd工作原理
内核定义了三个内存阈值(watermark,也称为水位),用来衡量当前剩余内存(pages_free)是否充裕或者紧张,分别是:
- 页最小阈值(pages_min)
- 页低阈值(pages_low)
- 页高阈值(pages_high)
三个内存阈值会划分为四种内存使用情况
kswapd 会定期扫描内存的使用情况,根据剩余内存(pages_free)的情况来进行内存回收的工作。
- 图中绿色部分:如果剩余内存(pages_free)大于 页高阈值(pages_high),说明剩余内存是充足的;
- 图中蓝色部分:如果剩余内存(pages_free)在页高阈值(pages_high)和页低阈值(pages_low)之间,说明内存有一定压力,但还可以满足应用程序申请内存的请求;
- 图中橙色部分:如果剩余内存(pages_free)在页低阈值(pages_low)和页最小阈值(pages_min)之间,说明内存压力比较大,剩余内存不多了。这时 kswapd0 会执行内存回收,直到剩余内存大于高阈值(pages_high)为止。虽然会触发内存回收,但是不会阻塞应用程序,因为两者关系是异步的。
- 图中红色部分:如果剩余内存(pages_free)小于页最小阈值(pages_min),说明用户可用内存都耗尽了,此时就会触发直接内存回收,这时应用程序就会被阻塞,因为两者关系是同步的。
可以看到,当剩余内存页(pages_free)小于页低阈值(pages_low),就会触发 kswapd 进行后台回收,然后 kswapd 会一直回收到剩余内存页(pages_free)大于页高阈值(pages_high) 也就是说 kswapd 的活动空间只有 pages_low 与 pages_min 之间的这段区域,如果剩余内测低于了 pages_min 会触发直接内存回收,高于了 pages_high 又不会唤醒 kswapd。
5.2 kswapd水位线
cat /proc/zoneinfo
cat /proc/sys/vm/min_free_kbytes
cat /proc/sys/vm/watermark_scale_factor
5.3 kswapd出现场景
5.3.1 ANR场景
参考:Android ANR & SWT_android swt-CSDN博客
5.3.2 Trace场景
在分析滑动丢帧、应用启动等性能场景的trace分析过程中,通常将kswapd进程作为当前系统低内存状态的重要标志之一,如下trace可以发现kswapd0进程异常活跃,在CPU运行片段占比最高,排行首位,证明当前系统已经处于低内存状态:
参考:Android 常用命令和工具解析之Trace相关-CSDN博客