工具
LeakCanary 数初,https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' 穩(wěn)定版,兼容6.0
BlockCanary:https://github.com/markzhai/AndroidPerformanceMonitor
compile 'com.github.markzhai:blockcanary-android:1.5.0'
重點:systrace + 函數(shù)插樁
Memory Analysis Tool(MAT)绍坝,
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
內(nèi)存檢查:
https://www.cnblogs.com/yezhennan/p/5442557.html
內(nèi)存耗用名詞解析:
VSS - Virtual Set Size 虛擬耗用內(nèi)存(包含共享庫占用的內(nèi)存)
RSS - Resident Set Size 實際使用物理內(nèi)存(包含共享庫占用的內(nèi)存)
PSS - Proportional Set Size 實際使用的物理內(nèi)存(比例分配共享庫占用的內(nèi)存)
USS - Unique Set Size 進(jìn)程獨自占用的物理內(nèi)存(不包含共享庫占用的內(nèi)存)
一般來說內(nèi)存占用大小有如下規(guī)律:VSS >= RSS >= PSS >= USS
OOM:
內(nèi)存泄露可以引發(fā)很多的問題:
1.程序卡頓宵荒,響應(yīng)速度慢(內(nèi)存占用高時JVM虛擬機(jī)會頻繁觸發(fā)GC)
2.莫名消失(當(dāng)你的程序所占內(nèi)存越大汁雷,它在后臺的時候就越可能被干掉。反之內(nèi)存占用越小报咳,在后臺存在的時間就越長)
3.直接崩潰(OutOfMemoryError)
ANDROID內(nèi)存面臨的問題:
1.有限的堆內(nèi)存侠讯,原始只有16M
2.內(nèi)存大小消耗等根據(jù)設(shè)備,操作系統(tǒng)等級暑刃,屏幕尺寸的不同而不同
3.程序不能直接控制
4.支持后臺多任務(wù)處理(multitasking)
5.運行在虛擬機(jī)之上
5R:
本文主要通過如下的5R方法來對ANDROID內(nèi)存進(jìn)行優(yōu)化:
1.Reckon(計算)
首先需要知道你的app所消耗內(nèi)存的情況厢漩,知己知彼才能百戰(zhàn)不殆
2.Reduce(減少)
消耗更少的資源
3.Reuse(重用)
當(dāng)?shù)谝淮问褂猛暌院螅M量給其他的使用
4.Review(檢查)
回顧檢查你的程序岩臣,看看設(shè)計或代碼有什么不合理的地方溜嗜。
5.Recycle(回收)
返回資源給生產(chǎn)流
下面從系統(tǒng)內(nèi)存(system ram)和堆內(nèi)存(heap)兩個方面介紹一些查看和計算內(nèi)存使用情況的方法:
1.系統(tǒng)內(nèi)存:
procstats,meminfo他們一個側(cè)重于后臺的內(nèi)存使用架谎,另一個是運行時的內(nèi)存使用炸宵。
procstats
2.Android 4.4 KitKat 提出了一個新系統(tǒng)服務(wù),叫做procstats狐树。它將幫助你更好的理解你app在后臺(background)時的內(nèi)存使用情況焙压。
可以通過adb shell命令去使用procstats(adb shell dumpsys procstats --hours 3),
或者更方便的方式是運行Process Stats開發(fā)者工具(在4.4版本的手機(jī)中點擊Settings > Developer options > Process Stats)
3.meminfo
Android還提供了一個工具叫做meminfo抑钟。它是根據(jù)PSS標(biāo)準(zhǔn) (Proportional Set Size——實際物理內(nèi)存)計算每個進(jìn)程的內(nèi)存使用并且按照重要程度排序涯曲。
你可以通過命令行去執(zhí)行它:(adb shell dumpsys meminfo)或者使用在設(shè)備上點擊Settings > Apps > Running(與Procstats不用,它也可以在老版本上運行)
命令查看內(nèi)存等
1.查看Dalvik內(nèi)存配置文件
adb pull /system/build.prop xxx 導(dǎo)入/system/build.prop到本地電腦
dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heapstartsize=8m 相當(dāng)于虛擬機(jī)的 -Xms配置在塔,該項用來設(shè)置堆內(nèi)存的初始大小幻件。
dalvik.vm.heapgrowthlimit=192m 相當(dāng)于虛擬機(jī)的 -XX:HeapGrowthLimit配置,該項用來設(shè)置一個標(biāo)準(zhǔn)的應(yīng)用的最大堆內(nèi)存大小蛔溃。一個標(biāo)準(zhǔn)的應(yīng)用就是沒有使用android:largeHeap的應(yīng)用绰沥。
dalvik.vm.heapsize=512m 相當(dāng)于虛擬機(jī)的 -Xmx配置篱蝇,該項設(shè)置了使用android:largeHeap的應(yīng)用的最大堆內(nèi)存大小。
2.shell查看
adb shell getprop|grep dalvik.vm.heapstartsize 應(yīng)用啟動后分配的初始內(nèi)存(某機(jī)型無效)
adb shell getprop|grep heapgrowthlimit 單個標(biāo)準(zhǔn)應(yīng)用程序最大內(nèi)存限制(沒設(shè)置android:largeHeap)
adb shell getprop|grep dalvik.vm.heapsize 單個dalvik虛擬機(jī)最大的內(nèi)存限制(設(shè)置了android:largeHeap)
Heap(堆內(nèi)存):
在程序中可以使用如下的方法去查詢內(nèi)存使用情況
ActivityManager#getMemoryClass()
ActivityManager#getMemoryInfo(ActivityManager.MemoryInfo)
得到的MemoryInfo中可以查看如下Field的屬性:
availMem:表示系統(tǒng)剩余內(nèi)存
lowMemory:它是boolean值徽曲,表示系統(tǒng)是否處于低內(nèi)存運行
hreshold:它表示當(dāng)系統(tǒng)剩余內(nèi)存低于好多時就看成低內(nèi)存運行
android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
dalvikPrivateDirty: The private dirty pages used by dalvik零截。
dalvikPss :The proportional set size for dalvik.
dalvikSharedDirty :The shared dirty pages used by dalvik.
nativePrivateDirty :The private dirty pages used by the native heap.
nativePss :The proportional set size for the native heap.
nativeSharedDirty :The shared dirty pages used by the native heap.
otherPrivateDirty :The private dirty pages used by everything else.
otherPss :The proportional set size for everything else.
otherSharedDirty :The shared dirty pages used by everything else.
說明:
dalvik:是指dalvik所使用的內(nèi)存。
native:是被native堆使用的內(nèi)存秃臣。應(yīng)該指使用C\C++在堆上分配的內(nèi)存涧衙。
other:是指除dalvik和native使用的內(nèi)存。但是具體是指什么呢奥此?至少包括在C\C++分配的非堆內(nèi)存弧哎,比如分配在棧上的內(nèi)存。
private:是指私有的稚虎。非共享的撤嫩。
share:是指共享的內(nèi)存。
PSS:實際使用的物理內(nèi)存(比例分配共享庫占用的內(nèi)存)
PrivateDirty:它是指非共享的蠢终,又不能換頁出去(can not be paged to disk )的內(nèi)存的大小序攘。比如Linux為了提高分配內(nèi)存速度而緩沖的小對象,即使你的進(jìn)程結(jié)束蜕径,該內(nèi)存也不會釋放掉两踏,它只是又重新回到緩沖中而已败京。
SharedDirty:參照PrivateDirty我認(rèn)為它應(yīng)該是指共享的兜喻,又不能換頁出去(can not be paged to disk )的內(nèi)存的大小。比如Linux為了提高分配內(nèi)存速度而緩沖的小對象赡麦,即使所有共享它的進(jìn)程結(jié)束朴皆,該內(nèi)存也不會釋放掉,它只是又重新回到緩沖中而已泛粹。
android.os.Debug#getNativeHeapSize()
返回的是當(dāng)前進(jìn)程navtive堆本身總的內(nèi)存大小
android.os.Debug#getNativeHeapAllocatedSize()
返回的是當(dāng)前進(jìn)程navtive堆中已使用的內(nèi)存大小
android.os.Debug#getNativeHeapFreeSize()
返回的是當(dāng)前進(jìn)程navtive堆中已經(jīng)剩余的內(nèi)存大小
技巧:android:largeHeap="true”(小心使用)
@@@@@@@@@@@@@@@@@@@@@@@@@@
需要特別注意優(yōu)化的點
1.Bitmap:
Bitmap是內(nèi)存消耗大戶遂铡,絕大多數(shù)的OOM崩潰都是在操作Bitmap時產(chǎn)生的,下面來看看如何幾個處理圖片的方法:
bitmap緩存:
內(nèi)存緩存(LruCache)
硬盤緩存(DiskLruCache)
根據(jù)需求去加載圖片的大小晶姊。
例如在列表中僅用于預(yù)覽時加載縮略圖(thumbnails )扒接。
只有當(dāng)用戶點擊具體條目想看詳細(xì)信息的時候,這時另啟動一個fragment/activity/對話框等等们衙,去顯示整個圖片
圖片大屑卣:
直接使用ImageView顯示bitmap會占用較多資源,特別是圖片較大的時候蒙挑,可能導(dǎo)致崩潰宗侦。
使用BitmapFactory.Options設(shè)置inSampleSize, 這樣做可以減少對系統(tǒng)資源的要求。
屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一忆蚀,即如果這個值為2矾利,則取出的縮略圖的寬和高都是原始圖片的1/2姑裂,圖片大小就為原始大小的1/4。
圖片像素:
Android中圖片有四種屬性男旗,分別是:
ALPHA_8:每個像素占用1byte內(nèi)存
ARGB_4444:每個像素占用2byte內(nèi)存
ARGB_8888:每個像素占用4byte內(nèi)存 (默認(rèn))
RGB_565:每個像素占用2byte內(nèi)存
Android默認(rèn)的顏色模式為ARGB_8888舶斧,這個顏色模式色彩最細(xì)膩,顯示質(zhì)量最高察皇。但同樣的捧毛,占用的內(nèi)存也最大。 所以在對圖片效果不是特別高的情況下使用RGB_565(565沒有透明度屬性)
圖片回收:
使用Bitmap過后让网,就需要及時的調(diào)用Bitmap.recycle()方法來釋放Bitmap占用的內(nèi)存空間呀忧,而不要等Android系統(tǒng)來進(jìn)行釋放。
捕獲異常:
經(jīng)過上面這些優(yōu)化后還會存在報OOM的風(fēng)險溃睹,所以下面需要一道最后的關(guān)卡——捕獲OOM異常:
2.強(qiáng)引用>軟引用>弱引用>虛引用
強(qiáng)引用(strong reference)
如:Object object=new Object()而账,object就是一個強(qiáng)引用了。當(dāng)內(nèi)存空間不足因篇,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯誤泞辐,使程序異常終止,也不會靠隨意回收具有強(qiáng)引用的對象來解決內(nèi)存不足問題竞滓。
軟引用(SoftReference)
只有內(nèi)存不夠時才回收,常用于緩存咐吼;當(dāng)內(nèi)存達(dá)到一個閥值,GC就會去回收它商佑;
弱引用(WeakReference)
弱引用的對象擁有更短暫的生命周期锯茄。在垃圾回收器線程掃描它 所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象茶没,不管當(dāng)前內(nèi)存空間足夠與否肌幽,都會回收它的內(nèi)存。
虛引用(PhantomReference)
"虛引用"顧名思義抓半,就是形同虛設(shè)喂急,與其他幾種引用都不同,虛引用并不會決定對象的生命周期笛求。如果一個對象僅持有虛引用廊移,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收探入。
3.縮小變量范圍
4.對常量使用static final修飾符
5.Context生命周期過長狡孔,
盡量避免static成員變量引用資源耗費過多的實例,
廣播
handler
toast
6避免創(chuàng)建不必要的對象
7.線程控制
8.UI Review(視圖檢查):
Android對于視圖中控件的布局渲染等會消耗很多的資源和內(nèi)存,所以這部分也是我們需要注意的新症。
減少視圖層級:
減少視圖層級可以有效的減少內(nèi)存消耗步氏,因為視圖是一個樹形結(jié)構(gòu),每次刷新和渲染都會遍歷一次徒爹。
hierarchyviewer:
想要減少視圖層級首先就需要知道視圖層級荚醒,所以下面介紹一個SDK中自帶的一個非常好用的工具h(yuǎn)ierarchyviewer芋类。
你可以在下面的地址找到它:your sdk path\sdk\tools
合理運用分屏,轉(zhuǎn)屏等
在視圖中加載你所需要的界阁,而不是你所擁有侯繁。因為用戶不可能同時看到所有東西。最典型的例子就是ListView中的滑動加載泡躯。
9.使用時再下載
====================恢復(fù)activity狀態(tài)
getLastNonConfigurationInstance贮竟,在oncreate或onstart里實例化,獲取onRetainNonConfigurationInstance中保存的實例
和onSaveInstanceState這個方法最大的好處是:
* 當(dāng)Activity曾經(jīng)通過某個資源得到一些圖片或者信息较剃,那么當(dāng)再次恢復(fù)后咕别,無需重新通過原始資源地址獲取,可以快速的加載整個Activity狀態(tài)信息写穴。
* 當(dāng)Activity包含有許多線程時惰拱,在變化后依然可以持有原有線程,無需通過重新創(chuàng)建進(jìn)程恢復(fù)原有狀態(tài)啊送。
* 當(dāng)Activity包含某些Connection Instance時,同樣可以在整個變化過程中保持連接狀態(tài)馋没。
注意:只做配置更改的優(yōu)化昔逗,依然要處理空指針的情況
下邊是需要特別注意的幾點:
1.使用有風(fēng)險
onRetainNonConfigurationInstance()
* The function will be called between {@link #onStop} and* {@link #onDestroy}.
如何加快應(yīng)用啟動速度
1.如果是6.0及以上,才考慮用閃屏篷朵。閃屏?xí)?00ms的優(yōu)化勾怒。
2.懶加載,針對不同客戶的加載款票。注意:防止集中化控硼,導(dǎo)致首頁加載后不可操作。
3.算法優(yōu)化艾少,
4.線程優(yōu)化,注意防止加鎖翼悴,導(dǎo)致線程阻塞缚够。一般用線程池管理線程○惺辏控制線程數(shù)谍椅,減少cpu調(diào)度,以及頻繁切換工作線程古话。
pipeline機(jī)制雏吭,根據(jù)業(yè)務(wù)優(yōu)先級定義業(yè)務(wù)初始化時機(jī)。為各個任務(wù)建立依賴,但是如果沒配置好腋妙,會導(dǎo)致主線程一直等待场躯。
注:參考框架:mmkernel套么、 alpha牵舵,
5.gc情況抖甘,debug結(jié)合allocation工具
Debug.startAllocCounting
6.啟動時盡量不要做系統(tǒng)調(diào)用爷绘,會占有cpu資源
Android崩潰入手
1.哪個進(jìn)程鲤屡、哪個線程孝治。是否主線程列粪,是前臺進(jìn)程,后臺進(jìn)程谈飒,還是系統(tǒng)進(jìn)程岂座。
2.是Java異常,native異常杭措,還是anr
關(guān)于性能的兩張圖掺逼。
以下圖片引自:https://time.geekbang.org/column/article/70250#previewimg