一蚯妇、前言
內(nèi)存問題主要會造成如下幾個影響。第一暂筝、會發(fā)生OOM等異常箩言,因?yàn)槊總€對象都會占用一定的內(nèi)存,內(nèi)存過多會影響對象的分配失敗焕襟,嚴(yán)重者會導(dǎo)致設(shè)備重啟陨收。第二、內(nèi)存過大鸵赖,導(dǎo)致系統(tǒng)回收緩存內(nèi)存务漩,以及會加快GC頻率,從而導(dǎo)致應(yīng)用卡頓它褪。
內(nèi)存的監(jiān)控就尤為的重要饵骨。下面從幾個方面來看Android對內(nèi)存的監(jiān)控,從而有助于觀察內(nèi)存變化茫打。
二居触、free
free是輕量級的查看設(shè)備整體內(nèi)存情況,具體例子如下:
root@debian7:/proc/10# free
total used free shared buffers cached
Mem: 3044840 247692 2797148 0 19896 110084
-/+ buffers/cache: 117712 2927128
Swap: 901116 0 901116
其中total = used + free包吝,單位KB饼煞。
對于-/+ buffers/cache行,是從有無緩沖來看诗越。117712 = used - buffers - cached.
2927128 = free + buffers + cached.
三、/proc/meminfo文件
/proc/meminfo是free的加強(qiáng)版息堂,free中的數(shù)據(jù)也是從/proc/meminfo而來的嚷狞。
root@debian7:/proc/10# cat /proc/meminfo
MemTotal: 3044840 kB // RAM內(nèi)存總大小
MemFree: 2797520 kB // RAM可用內(nèi)存大小
Buffers: 19912 kB // Buffers緩存,文件緩存
Cached: 110084 kB // Cached緩存大小
SwapCached: 0 kB
Active: 97184 kB // 在活躍下的緩沖或高速緩沖存儲器頁面文件的大小
Inactive: 94008 kB // 非活躍下的緩沖或高速緩沖存儲器頁面文件的大小
Active(anon): 61312 kB // Active = Active(anon) + Active(file)
Inactive(anon): 6008 kB
Active(file): 35872 kB
Inactive(file): 88000 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 901116 kB
SwapFree: 901116 kB
Dirty: 0 kB // 等待被寫回到磁盤的內(nèi)存大小荣堰。
Writeback: 0 kB // 正在被寫回到磁盤的內(nèi)存大小床未。
AnonPages: 61188 kB
Mapped: 24656 kB // 文件通過mmap分配的內(nèi)存
Shmem: 6132 kB
Slab: 20412 kB // 內(nèi)核數(shù)據(jù)結(jié)構(gòu)緩存的大小。Linux中的Slab內(nèi)存分配策略振坚,相對于伙伴系統(tǒng)分配
SReclaimable: 6288 kB
SUnreclaim: 14124 kB
KernelStack: 1000 kB
PageTables: 4656 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2423536 kB
Committed_AS: 535828 kB
VmallocTotal: 34359738367 kB // 總分配的虛擬地址空間
VmallocUsed: 158760 kB // 已分配的虛擬地址空間
VmallocChunk: 34359576572 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 45056 kB
DirectMap2M: 2076672 kB
DirectMap1G: 1048576 kB
四薇搁、vmstat
vmstat命令可以查看內(nèi)存、IO和CPU等信息渡八。
語法命令:
Usage: vmstat [ -n iterations ] [ -d delay ] [ -r header_repeat ]
-n iterations 數(shù)據(jù)循環(huán)輸出的次數(shù)
-d delay 兩次數(shù)據(jù)間的延遲時長(單位:S)
-r header_repeat 循環(huán)多少次啃洋,再輸出一次頭信息行
vmstat例子:
root@debian7:/proc/10# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 0 2797396 19960 110084 0 0 1 0 13 25 0 0 100 0
procs 程序
- r : 運(yùn)行的進(jìn)程數(shù)量
- b :等待IO阻塞的進(jìn)程數(shù)量
memory 內(nèi)存
- swpd : 虛擬內(nèi)存(swap空間)已使用的大小
- free :剩余的物理內(nèi)存大小
- buff : buff緩存大小
- cache :文件等cache大小
swap swap空間传货,內(nèi)存夠用時,si和so值都為0
- si : swap空間寫入內(nèi)存的數(shù)據(jù)量宏娄;
- so: 內(nèi)存寫入swap空間的數(shù)據(jù)量问裕;
IO
- bi : 每秒從塊設(shè)備讀取塊的數(shù)量
- bo:每秒向塊設(shè)備寫入的塊數(shù)量
system
- in : 每秒的中斷次數(shù)
- cs :等秒的上下文切換次數(shù)
cpu
- us : 用戶態(tài)執(zhí)行時間
- sy : 內(nèi)核態(tài)執(zhí)行時間
- id : 空閑時間(包括IO等待時間)
- wa : 等待IO時間
五、/proc/[PID]/status
root@p212:/data/dropbear # cat /proc/4943/status
Name: XXXXX
State: S (sleeping) : 狀態(tài)
Tgid: 4943 :線程組ID
Pid: 4943 :進(jìn)程ID孵坚,同TGID粮宛,說明是主線程
PPid: 3769 :父進(jìn)程ID
TracerPid: 0
Uid: 10039 10039 10039 10039
Gid: 10039 10039 10039 10039
Ngid: 0
FDSize: 64 :FDSize是當(dāng)前分配過的文件描述符數(shù)量
Groups: 3003 9997 50039 : groups表示啟動這個進(jìn)程的用戶所在的組.
VmPeak: 1503968 kB :當(dāng)前進(jìn)程運(yùn)行過程中占用內(nèi)存的峰值
VmSize: 878224 kB :虛擬內(nèi)存大小
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 40040 kB :分配到物理內(nèi)存的峰值
VmRSS: 38120 kB : 虛擬內(nèi)存駐留集合大小
VmData: 134084 kB :進(jìn)程數(shù)據(jù)段的大小
VmStk: 8196 kB : 進(jìn)程堆棧段的大小
VmExe: 20 kB
VmLib: 69224 kB
VmPTE: 440 kB
VmSwap: 0 kB : 進(jìn)程占用Swap的大小.
Threads: 22 :線程數(shù)量
SigQ: 0/2462
SigPnd: 0000000000000000 : 存儲了該線程的待處理信號
ShdPnd: 0000000000000000 : 存儲了該線程組的待處理信號
SigBlk: 0000000000001204 : 存放被阻塞的信號
SigIgn: 0000000000000000 : 存放可被忽略的信號
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
Seccomp: 0
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 358 : 進(jìn)程主動切換上下文的次數(shù)(資源得不到等)
nonvoluntary_ctxt_switches: 400 : 進(jìn)程被動切換上下文的次數(shù).
六、Android#Runtime
Android中提供了一些接口卖宠,供使用者調(diào)用巍杈,可定時的采集以下幾種內(nèi)存情況,從而判斷當(dāng)前進(jìn)程內(nèi)存情況扛伍。
Runtime runtime = Runtime.getRuntime();
long javaMax = runtime.maxMemory(); // JVM可分配的最大內(nèi)存
long javaTotal = runtime.totalMemory(); // 當(dāng)前分配的內(nèi)存
long javaUsed = javaTotal - runtime.freeMemory(); // 當(dāng)前使用的內(nèi)存
float proportion = (float) javaUsed / javaMax;
Log.e("TAG", "onResume: javaMax="+javaMax+";javaTotal="+javaTotal+";javaUsed="+javaUsed+";proportion="+proportion);
七秉氧、Android#onLowMemory
在Android4.0中提供了一些監(jiān)聽內(nèi)存的接口OnLowMemory和onTrimMemory
【一】、OnLowMemory
OnLowMemory是ComponentCallbacks接口中的方法蜒秤,當(dāng)系統(tǒng)內(nèi)存不足汁咏,要被殺死后臺程序時,會調(diào)用該方法作媚。
可用在Application攘滩、Activity、Fragement纸泡、Service和ContentProvider中
【二】漂问、onTrimMemory
因?yàn)镺nLowMemory的接口太簡單了,并沒有提供內(nèi)存的狀態(tài)女揭,在ComponentCallbacks2中豐富了回調(diào)了接口蚤假。
public interface ComponentCallbacks2 extends ComponentCallbacks {
/** @hide */
@IntDef(prefix = { "TRIM_MEMORY_" }, value = {
TRIM_MEMORY_COMPLETE,
TRIM_MEMORY_MODERATE,
TRIM_MEMORY_BACKGROUND,
TRIM_MEMORY_UI_HIDDEN,
TRIM_MEMORY_RUNNING_CRITICAL,
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_MODERATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrimMemoryLevel {}
static final int TRIM_MEMORY_COMPLETE = 80;
static final int TRIM_MEMORY_MODERATE = 60;
static final int TRIM_MEMORY_BACKGROUND = 40;
static final int TRIM_MEMORY_UI_HIDDEN = 20;
static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
static final int TRIM_MEMORY_RUNNING_LOW = 10;
static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
void onTrimMemory(@TrimMemoryLevel int level);
}
同onLowMemory一致,也可用在Application吧兔、Activity磷仰、Fragement、Service和ContentProvider中境蔼。
- TRIM_MEMORY_COMPLETE:內(nèi)存不足灶平,并且該進(jìn)程在后臺進(jìn)程列表最后一個,馬上就要被清理
- TRIM_MEMORY_MODERATE:內(nèi)存不足箍土,并且該進(jìn)程在后臺進(jìn)程列表的中部逢享。
- TRIM_MEMORY_BACKGROUND:內(nèi)存不足,并且該進(jìn)程是后臺進(jìn)程吴藻。
- TRIM_MEMORY_UI_HIDDEN:內(nèi)存不足瞒爬,并且該進(jìn)程的UI已經(jīng)不可見了。
- TRIM_MEMORY_RUNNING_CRITICAL:內(nèi)存不足,并且該進(jìn)程不是消耗性的后臺進(jìn)程侧但,需要清理內(nèi)存
- TRIM_MEMORY_RUNNING_LOW:內(nèi)存不足矢空,并且該進(jìn)程不是消耗性的后臺進(jìn)程,需要清理內(nèi)存
- TRIM_MEMORY_RUNNING_MODERATE:內(nèi)存不足俊犯,并且該進(jìn)程不是消耗性的后臺進(jìn)程妇多,需要清理內(nèi)存
八、dumpsys
通過adb shell dumpsys meminfo [pid | 包名] 可以查看單個APP內(nèi)存情況
如下:
Applications Memory Usage (kB):
Uptime: 31564143 Realtime: 31564143
** MEMINFO in pid 30712 [XXX] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2196 2092 0 0 6144 4855 1288
Dalvik Heap 1517 1396 0 0 3873 2449 1424
Dalvik Other 373 372 0 0
Stack 132 132 0 0
Ashmem 2 0 0 0
Other dev 5 0 4 0
.so mmap 1148 96 156 0
.apk mmap 326 0 28 0
.ttf mmap 88 0 76 0
.dex mmap 2844 4 2840 0
.oat mmap 1204 0 164 0
.art mmap 1321 476 368 0
Other mmap 11 8 0 0
GL mtrack 2960 2960 0 0
Unknown 121 120 0 0
TOTAL 14248 7656 3636 0 10017 7304 2712
App Summary
Pss(KB)
------
Java Heap: 2240
Native Heap: 2092
Code: 3364
Stack: 132
Graphics: 2960
Private Other: 504
System: 2956
TOTAL: 14248 TOTAL SWAP (KB): 0
Objects
Views: 15 ViewRootImpl: 1
AppContexts: 2 Activities: 1
Assets: 2 AssetManagers: 2
Local Binders: 9 Proxy Binders: 12
Parcel memory: 3 Parcel count: 14
Death Recipients: 0 OpenSSL Sockets: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
第一部分主要字段解析:
- Native Heap : Native堆大小
- Dalvik Heap : java堆大小
- Stack :棧大小
- Ashmem : 匿名共享內(nèi)存大小
- .so mmap : 映射的so庫大小
- .apk mmap:apk占用內(nèi)存大小
- .dex mmap : dex占用內(nèi)存大小
- Pss Total :物理內(nèi)存總大小
- Private Dirty : 進(jìn)程私有的內(nèi)存燕侠,相對磁盤數(shù)據(jù)有改動的內(nèi)存
- Private Clean : 進(jìn)程私有的內(nèi)存者祖,相對磁盤數(shù)據(jù)沒有修改的內(nèi)存
- Heap Size : Dalvik中,同runtime.totalMemory()
- Heap Alloc : Dalvik中绢彤,同runtime.totalMemory()-runtime.freeMemory()
- Heap Free : Dalvik中七问,同runtime.freeMemory()
App Summary主要字段解析:
- Java Heap : Dalvik Heap 中的 Private Dirty + .art mmap 的 Private Dirty + Private Clean
- Native Heap :Native Heap
- Code : .so mmap+ .jar mmap + .apk mmap + .ttf mmap + .dex mmap + .oat mmap的 Private Dirty + Private Clean
- Stack :Stack
Objects主要字段解析:
- Views :存活的view的個數(shù)
- ViewRootImpl : 存活的ViewRootImpl數(shù)量
- AppContexts :APP整個上下文數(shù)量
- Activities : 存活的Activity數(shù)量