為什么進(jìn)行內(nèi)存優(yōu)化?
1. app運(yùn)行內(nèi)存限制,OOM導(dǎo)致app奔潰
2. app性能:流暢性、響應(yīng)速度和用戶體驗(yàn)
獲取Android系統(tǒng)默認(rèn)給每個(gè)app分配的內(nèi)存上限: ? ? ??
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);int memoryClass = activityManager.getMemoryClass();? ? //單位為m 經(jīng)測試本人紅米3S為192M
如果超出這個(gè)限制,將會導(dǎo)致應(yīng)用OOM锭弊。
Android的內(nèi)存管理方式:
?Android系統(tǒng)的內(nèi)存分配和回收方式:
1. Android底層是Linux系統(tǒng),一個(gè) App 通常就是一個(gè)進(jìn)程對應(yīng)一個(gè)虛擬機(jī),一個(gè)虛擬機(jī)在linux系 統(tǒng)中有一個(gè)進(jìn)程與之對應(yīng)
2. GC(垃圾回收器)只在Heap(堆)剩余空間不夠時(shí)才會發(fā)出垃圾回收
3. GC觸發(fā)時(shí),所有的線程都會被暫停
App 內(nèi)存限制機(jī)制:
1. 每個(gè) App 分配的最大內(nèi)存限制,隨不同設(shè)備而不同
2. 吃內(nèi)存大戶:圖片
3. 為什么要限制:多任務(wù)系統(tǒng)要使每個(gè) App?具有足夠的內(nèi)存空間保證都能夠正常運(yùn)行
切換應(yīng)用時(shí)后臺 App?清理機(jī)制:
1. 后臺各種 App?切換時(shí)的LRU Cache(最近使用的排在最前面,最少使用的可能被清理掉)
2. 系統(tǒng)內(nèi)存不夠清理后臺 App?時(shí)候回調(diào)onTrimMemory(int level)方法
onTrimMemory(int level):
? ? ?4.0 之后提供的一個(gè)API旁舰,這個(gè) API 是提供給開發(fā)者的定庵,它的主要作用是提示開發(fā)者在系統(tǒng)內(nèi)存不足的時(shí)候秤朗,通過處理部分資源來釋放內(nèi)存肯腕,從而避免被 Android 系統(tǒng)殺死,提高應(yīng)用程序的用戶體驗(yàn).當(dāng)內(nèi)存變化時(shí)Android系統(tǒng)會根據(jù)不同等級的內(nèi)存使用情況步脓,調(diào)用這個(gè)函數(shù),并傳入對應(yīng)的等級;我們可以根據(jù)該等級的類型观谦,如果等級屬于危險(xiǎn)級別的就可以做相應(yīng)的處理,把我們 App?里面不同的內(nèi)存盡快清理掉,這樣我們的 App?內(nèi)存占用就相對小一點(diǎn),從而降低被清理掉的可能性拉盾。
監(jiān)控內(nèi)存的幾種方法:
1. 代碼:以下兩個(gè)相加就是系統(tǒng)當(dāng)前分配給這個(gè) App?的內(nèi)存大小
已經(jīng)分配的內(nèi)存大小:
float totalMemory = Runtime.getRuntime().totalMemory() * 1.0f / (1024 * 1024);
空閑的內(nèi)存:
float freeMemory = Runtime.getRuntime().freeMemory() * 1.0f / (1024 * 1024);
2. AS的Android Monitor監(jiān)控:
3. AS的Android Device Monitor
? ? 依次點(diǎn)擊:Tools-->Android-->Android Device Monitor豁状,選中要查看的進(jìn)程,點(diǎn)擊左上角綠色水杯Update Heap-->右邊Cause GC捉偏;
? ? Heap Size:堆的大小 = Allocated:分配的大小 + Free:空閑的大小泻红;
? ? data Object和class Object如果不穩(wěn)定夭禽、不斷變大就考慮是否內(nèi)存泄漏
Android內(nèi)存優(yōu)化方法
數(shù)據(jù)結(jié)構(gòu)優(yōu)化:
1. 頻繁字符串拼接用StringBuilder
? 字符串通過+的方式進(jìn)行拼接,會產(chǎn)生中間字符串內(nèi)存塊,會被GC回收掉,同時(shí)也是效率低的,耗時(shí)較長
2. ArrayMap、SparseAray代替HashMap
3. 內(nèi)存抖動:變量使用不當(dāng)引起
? ? 比如突然申請很多變量或內(nèi)存空間,但是這個(gè)內(nèi)存空間的數(shù)據(jù)很快就用完了,過了一會又申請很多,這時(shí)如果heap的內(nèi)存不夠,GC就會回收之前的內(nèi)存,這個(gè)過程會暫停所有線程,如果這個(gè)過程在較短的時(shí)間內(nèi)重復(fù)出現(xiàn)可以從Monitor中看到鋸齒一樣的抖動,如果內(nèi)存抖動耗時(shí)比較長,就會影響 App?運(yùn)行的流暢性,用戶體驗(yàn)就會很差
4. 再小的Class也至少耗費(fèi)0.5KB
5. HashMap一個(gè)entry需要額外占用32B
6. 避免在android里面使用Enum
7. 減少bitmap的內(nèi)存占用,圖片質(zhì)量不高時(shí)可以考慮使用RBG_565
8. 萬惡的static
? ? static聲明變量的生命周期和 App?的生命周期一樣的,大量的使用谊路,會占據(jù)內(nèi)存空間不釋放讹躯,積少成多會造成內(nèi)存的不斷開銷,直至掛掉;
? ? 一般用來修飾基本數(shù)據(jù)類型或者輕量級對象缠劝,盡量避免修復(fù)集合或者大對象蜀撑,常用作修飾全局配置項(xiàng)、工具類方法剩彬、內(nèi)部類酷麦。
對象復(fù)用:
1. 復(fù)用系統(tǒng)自帶的資源(布局、資源文件等)
2. listView喉恋、GridView的convertView復(fù)用(現(xiàn)在都在用RecyclerView了吧哈~)
3. 避免在onDraw()方法里面執(zhí)行對象的創(chuàng)建
避免內(nèi)存泄漏:
1. 內(nèi)存泄漏會導(dǎo)致剩余可用 heap 內(nèi)存越來越少,頻繁觸發(fā)GC直至OOM
2. 尤其activity泄漏
? ? 被靜態(tài)集合引用沃饶;
? ? 內(nèi)部類中開啟線程會保持外部activity的引用,如果執(zhí)行耗時(shí)操作,當(dāng)activity銷毀時(shí)還未執(zhí)行完畢導(dǎo)致activity無法被回收,導(dǎo)致內(nèi)存泄漏。
3. 盡量用Application Context而不是Activity Context
4. 注意Cursor對象是否及時(shí)關(guān)閉
5. 謹(jǐn)慎handler
? ? Activity的onDestroy方法中調(diào)用handler.removeCallbacksAndMessages(null);取消所有的消息的處理轻黑,包括待處理的消息糊肤;
? ? 聲明handler的內(nèi)部類為static。
6. 頁面背景和圖片加載
? ? 設(shè)置背景和圖片的時(shí)候氓鄙,如果是純色馆揉,盡量使用color
? ? 規(guī)則圖形,盡量使用shape畫圖
? ? 稍微復(fù)雜點(diǎn)抖拦,可以使用9patch圖
? ? 不能使用9patch的情況下升酣,針對幾種主流分辨率的機(jī)型進(jìn)行切圖
內(nèi)存泄漏:邏輯上創(chuàng)建的對象(內(nèi)存塊)使用完后不用了,應(yīng)該被GC回收變成Free內(nèi)存,但由于代碼瑕疵,導(dǎo)致這塊內(nèi)存雖然不用了,但仍然被其他地方引用著,使得GC無法對其進(jìn)行回收态罪,大量的內(nèi)存泄露會導(dǎo)致OOM噩茄。
OOM 問題優(yōu)化:
OOM問題分析:
1. OOM的必然性和可解決性
? ? 程序開發(fā)的性能不好,超出最大內(nèi)存限制就必然發(fā)生,手機(jī)廠家在設(shè)定內(nèi)存上限的時(shí)候肯定已經(jīng)考慮過了,你的 App?如果優(yōu)化的比較好,肯定可以在手機(jī)上順暢的運(yùn)行,不會出現(xiàn)OOM問題,出現(xiàn)該問題肯定是優(yōu)化有問題。
2. OOM絕大部分發(fā)生在圖片
強(qiáng)引用复颈、軟引用:
變量的生命周期:跟其所處的環(huán)境有關(guān)绩聘,一般變量在activity的成員位置,那么他的生命周期和activity是一樣的,如果在方法里面,它的生命周期跟方法有關(guān)凿菩,方法退出了,其生命周期就結(jié)束了机杜;當(dāng)變量生命周期終止的時(shí)候,GC是會對它進(jìn)行回收的衅谷。
被軟引用包裹的變量:softReference<類型>椒拗,假設(shè)其在activity成員位置,如果在activity的活動的生命周期中会喝,內(nèi)存不夠了,對于軟引用所占有的內(nèi)存空間GC是可以對其進(jìn)行回收的,所以軟引用和強(qiáng)引用的關(guān)系是:
? ?強(qiáng)引用在生命周期里它的內(nèi)存空間是不會被回收的玩郊,而軟引用在它的生命周期里肢执,只要是內(nèi)存不夠了,那么GC是可以對其進(jìn)行回收的译红,所以在運(yùn)行的過程中有可能會出現(xiàn)這個(gè)引用變成空的了它不再指向任何一個(gè)內(nèi)存空間预茄。
優(yōu)化OOM問題的方法
1. 注意臨時(shí)Bitmap對象的及時(shí)回收(置空或recycler)GC會很快處理掉
2. 避免Bitmap浪費(fèi)
3. Try catch? 某些大內(nèi)存分配的操作
4. 加載Bitmap:縮放比例(采樣率)、解碼格式(RGB_565))侦厚、局部加載(解決大部分圖片相關(guān)OOM問題)
5. 最后推薦一個(gè)檢測 App?內(nèi)存泄露的工具
? LeakCanary(具體使用網(wǎng)上很容易查到耻陕,很簡單就可以集成,就不贅述了)