前言
Android 线上、线下获取内存值的方式。
Android Q 对于线上客户端数据采集有影响,在文中有说明。
线下 adb shell
1. cat /proc [ /meminfo | /<pid>/meminfo ]
案例:cat proc/meminfo 获取手机整体内存信息
dipper:/ $ cat /proc/meminfo
MemTotal: 5764820 kB
MemFree: 134792 kB
MemAvailable: 2791948 kB
Buffers: 198508 kB
Cached: 2039260 kB
SwapCached: 30748 kB
Active: 2687728 kB
Inactive: 1038232 kB
Active(anon): 1203052 kB
Inactive(anon): 460412 kB
Active(file): 1484676 kB
Inactive(file): 577820 kB
Unevictable: 161012 kB
Mlocked: 161012 kB
SwapTotal: 2621436 kB
SwapFree: 2022464 kB
Dirty: 548 kB
Writeback: 0 kB
AnonPages: 1643692 kB
Mapped: 866444 kB
Shmem: 14664 kB
Slab: 454188 kB
SReclaimable: 198228 kB
SUnreclaim: 255960 kB
KernelStack: 77744 kB
PageTables: 105048 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 5503844 kB
Committed_AS: 99980240 kB
VmallocTotal: 263061440 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
CmaTotal: 204800 kB
CmaFree: 156 kB
2. dumpsys meminfo <pid | processName>
** 通过 dumpsys meminfo -h 可以获取帮助**
dipper:/ $ 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.
案例: 获取指定 app 的内存信息
dipper:/ $ dumpsys meminfo -a com.xiaomi.xmsf
Applications Memory Usage (in Kilobytes):
Uptime: 3335578435 Realtime: 4039602043
** MEMINFO in pid 7549 [com.xiaomi.xmsf] **
Pss Pss Shared Private Shared Private SwapPss Heap Heap Heap
Total Clean Dirty Dirty Clean Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------ ------ ------
Native Heap 4136 0 532 4124 0 0 1375 17484 15882 1601
Dalvik Heap 2604 0 284 2596 0 0 63 3740 1870 1870
Dalvik Other 733 0 100 732 0 0 26
Stack 24 0 4 24 0 0 12
Ashmem 0 0 12 0 0 0 0
Other dev 65 0 268 0 0 64 0
.so mmap 405 0 768 44 11348 0 21
.jar mmap 261 0 0 0 10256 0 0
.apk mmap 0 0 0 0 20 0 0
.dex mmap 2448 2444 0 4 4 2444 4
.oat mmap 1205 0 0 0 39116 0 0
.art mmap 1615 4 11488 1348 116 4 202
Other mmap 20 0 32 12 160 0 0
Unknown 756 0 348 748 0 0 326
TOTAL 16301 2448 13836 9632 61020 2512 2029 21224 17752 3471
Dalvik Details
.Heap 2112 0 0 2112 0 0 0
.LOS 16 0 0 16 0 0 15
.Zygote 460 0 284 452 0 0 48
.NonMoving 16 0 0 16 0 0 0
.LinearAlloc 313 0 56 312 0 0 22
.GC 304 0 32 304 0 0 4
.IndirectRef 116 0 12 116 0 0 0
.Boot vdex 0 0 0 0 4 0 0
.App dex 1596 1592 0 4 0 1592 4
.App vdex 852 852 0 0 0 852 0
.App art 548 4 0 544 0 4 52
.Boot art 1067 0 11488 804 116 0 150
App Summary
Pss(KB)
------
Java Heap: 3948
Native Heap: 4124
Code: 2492
Stack: 24
Graphics: 0
Private Other: 1556
System: 4157
TOTAL: 16301 TOTAL SWAP PSS: 2029
Objects
Views: 2 ViewRootImpl: 0
AppContexts: 5 Activities: 0
Assets: 17 AssetManagers: 0
Local Binders: 28 Proxy Binders: 35
Parcel memory: 57 Parcel count: 65
Death Recipients: 1 OpenSSL Sockets: 5
WebViews: 0
SQL
MEMORY_USED: 330
PAGECACHE_OVERFLOW: 42 MALLOC_SIZE: 117
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 20 57 2/18/3 /data/user/0/com.xiaomi.xmsf/databases/crash.db
4 16248 21 253/153/2 /data/user/0/com.xiaomi.xmsf/databases/traffic.db
线上安卓客户端
1. ActivityManager.getProcessMemoryInfo(pids: IntArray)
在 Android Q 之后只能获取同 uid 进程的内存数据,而且频率受系统底层限制,默认是 300000 ms (5 min)。
可以通过 adb shell settings put global activity_manager_constants ‘memory_info_throttle_time=1000’ 修改底层的频率限制。(需要开启 USB 调试的安全调试模式)
/**
* 通过 ActivityManager 获取进程的内存信息,
* 但是在 Android Q 以上受系统底层限制,连续频繁采样会获取一样的数据,
* 具体间隔可以通过 getMemoryInfoThrottleTime(context: Context) 获取。
*
* @see getMemoryInfoThrottleTime(context: Context)
*
* @return 返回精准的 pss 内存信息
*/
fun getThrottledMemoryInfo(context: Context, pid: Int = myPid()): Debug.MemoryInfo {
val memInfo: Debug.MemoryInfo?
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memInfos: Array<Debug.MemoryInfo> = manager.getProcessMemoryInfo(IntArray(1) { pid })
memInfo = memInfos[0]
return memInfo
}
private const val SETTINGS_NAME = "activity_manager_constants"
private const val MEMORY_INFO_THROTTLE_TIME_KEY_PATTERN = "memory_info_throttle_time="
private const val THROTTLE_VALUE_NAME_PATTERN = "$MEMORY_INFO_THROTTLE_TIME_KEY_PATTERN\\d*"
// 安卓官方默认的采集间隔是 300 s
private const val DEFAULT_THROTTLE_TIME = 300000L
// 获取系统的 memory_info_throttle_time 的数值
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
fun getMemoryInfoThrottleTime(context: Context): Long {
val resolver = context.contentResolver
val constants = Settings.Global.getString(resolver, SETTINGS_NAME)
var value = DEFAULT_THROTTLE_TIME
if (constants != null) {
val regex = Regex(THROTTLE_VALUE_NAME_PATTERN)
val find = regex.find(constants)
if (find != null && find.groupValues.isNotEmpty()) {
val throttle = find.groupValues[0]
val valueStr = throttle.substring(MEMORY_INFO_THROTTLE_TIME_KEY_PATTERN.length, throttle.length)
value = try {
valueStr.toLong()
} catch (e: NumberFormatException) {
DEFAULT_THROTTLE_TIME
}
}
}
return value
}
2. Debug.getMemoryInfo(memInfo: Debug.MemoryInfo)
只能获取当进程的内存信息。但在 Android Q 之后依旧可以频繁调用。
会缺失 graphicis 的数值,其实最后返回的 totalPss 数值是偏小的。
/**
* 在 Android Q 以上也可以及时获取最新内存信息的方法,
* 但存在以下问题
* 1. 功耗较高,耗时波动在 40ms - 120ms 之间。
* 2. 缺失 graphics 的数值,在 GPU 占用高的场景与真实数值差距较大。
*
* @return 返回 pss 内存信息,
*/
fun getDebugMemoryInfo(): Debug.MemoryInfo {
val memInfo = Debug.MemoryInfo()
Debug.getMemoryInfo(memInfo)
return memInfo
}