Android 內(nèi)存管理與優(yōu)化

1.Android 內(nèi)存基礎(chǔ)

  • 所有的內(nèi)存都是基于物理內(nèi)存的淘讥,即移動設(shè)備上的RAM甚带。當啟動一個Android程序時鸟召,會啟動一個Dalvik vm 進程,系統(tǒng)會給它分配固定的內(nèi)存空間了牛,這塊內(nèi)存會映射到RAM上某個區(qū)域颜屠,然后Android程序就運行在這塊空間上。JAVA里會將這塊空間分成Stack棧內(nèi)存Heap堆內(nèi)存鹰祸。stack里存放對象的引用甫窟,heap里存放實際對象數(shù)據(jù)。

2.Android 問題與調(diào)節(jié)

  • Memory Leak(內(nèi)存泄露):在程序中創(chuàng)建對象蛙婴,不及時回收無效空間粗井。
  • OutOfMemory(內(nèi)存溢出):當應(yīng)用程序申請的java heap空間超過Dalvik vm HeapGrowthLimit時,溢出街图。(內(nèi)存溢出并不是內(nèi)存不夠浇衬,而是超過了Dalvik vm的限制)
  • Memory Churn(內(nèi)存抖動):GC操作頻繁,導致內(nèi)存抖動餐济。
  • 調(diào)節(jié):如果RAM申請的空間不足耘擂,那么Android的Memory Killer會殺死優(yōu)先級低的進程。

3.Android默認內(nèi)存回收機制——垃圾回收GC(Garbage Collection)

  • 進程優(yōu)先級:
    1. Foreground:處于焦點的進程絮姆。
    2. Visible:可以看見的進程醉冤。
    3. Service:服務(wù)進程。
    4. Background:后臺進程篙悯。
    5. Empty:空進程蚁阳。
  • 進行GC時刻:
    1. app空閑的時候。
    2. 內(nèi)存緊張的時候 鸽照。
    3. 分配大的內(nèi)存塊不夠用的時候韵吨。
  • GC回收模型:
    Generational Heap Memory
    采用分代技術(shù),分為年輕代移宅、老年代、持久代椿疗。

    最先清理的是年輕代漏峰,只要清理出足夠的空間就不去清理老年代了。依次持久帶也是這樣届榄。
  • GC回收步驟:
    1. 從GC Root開始浅乔,開始對對象引用遍歷,找出GC Root直接引用的所有對象集1,再找出直接引用對象集1的對象集2……直到遍歷完所有對象靖苇。這些對象都是有效對象席噩。
    2. 刪除其他無效對象。
  • 注:GC執(zhí)行的時候贤壁,當前所有線程的任何操作都會需要暫停悼枢。

4.優(yōu)化——減少額外的內(nèi)存使用

4.1 Bitmap

基本思路:縮略圖,像素脾拆,圖片回收馒索,捕獲OOM異常

  • 縮略圖:使用BitmapFactory.Options方法里的inSampleSize設(shè)置縮小比。
  • 像素:
    Android中圖片有四種屬性名船,分別是:
    ALPHA_8:每個像素占用1byte內(nèi)存
    ARGB_4444:每個像素占用2byte內(nèi)存
    ARGB_8888:每個像素占用4byte內(nèi)存 (默認)
    RGB_565:每個像素占用2byte內(nèi)存
    默認是顯示效果最好的ARGB_8888绰上,可以使用BitmapFactory.OptionsinPreferredConfig方法設(shè)置。
  • 圖片回收:
// 先判斷是否已經(jīng)回收  
if(bitmap != null && !bitmap.isRecycled()){  
    // 回收并且置為null  
    bitmap.recycle();  
    bitmap = null;  
}  
System.gc();  
  • 捕獲OOM異常:
Bitmap bitmap = null;  
try {  
    // 實例化Bitmap  
    bitmap = BitmapFactory.decodeFile(path);  
} catch (OutOfMemoryError e) {  
    // 捕獲OutOfMemoryError渠驼,避免直接崩潰  
}  
if (bitmap == null) {  
    // 如果實例化失敗 返回默認的Bitmap對象  
    return defaultBitmapMap;  
}  

4.2 軟引用和弱引用

軟件用和弱引用的概念解釋

  • 想避免OutOfMemory異常的發(fā)生蜈块,則可以使用軟引用。
  • 想要提高應(yīng)用性能迷扇,盡快回收占用內(nèi)存更大的對象百揭,可以使用弱引用。

4.3 小Tips

  • Context的引用谋梭,具體請看這篇Android Context詳解信峻。
  • 避免創(chuàng)建不必要的對象:你要頻繁操作一個字符串時,使用StringBuffer代替String瓮床。
  • 避免使用浮點數(shù):在Android設(shè)備中盹舞,浮點數(shù)會比整型慢兩倍。
  • 循環(huán):盡量避免在for的條件參數(shù)(即())中訪問成員變量隘庄,調(diào)用方法踢步,例如:
for(int i=0;i<a.length;i++)//訪問成員變量,會慢很多丑掺。
for(int i=0;i<a.length();i++)//調(diào)用方法获印,也會慢很多。

正確的做法如下:

int b = a.length/int b=a.length();
for(int i=0;i<b;i++)//這樣循環(huán)體最快街州。

java還有一種訪問數(shù)組的方法,這種方法比普通循環(huán)多出4個字節(jié)兼丰,因為產(chǎn)生了一個額外的變量,如下列的a唆缴。

for(Foo a:Array){
    m+=a.value;
}
  • 了解并使用類庫
    當你在處理字串的時候鳍征,不要吝惜使用String.indexOf()String.lastIndexOf()等特殊實現(xiàn)的方法面徽。這些方法都是使用C/C++實現(xiàn)的艳丛,比起Java循環(huán)快10到100倍匣掸。
    android.text.format包下的Formatter類,提供了IP地址轉(zhuǎn)換氮双、文件大小轉(zhuǎn)換等方法碰酝;DateFormat類,提供了各種時間轉(zhuǎn)換戴差,都是非常高效的方法送爸。
    TextUtils類,對于字符串處理Android為我們提供了一個簡單實用的TextUtils類造挽,如果處理比較簡單的內(nèi)容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類碱璃。

5.優(yōu)化—— 重復使用內(nèi)存資源

  • 將已經(jīng)存在的內(nèi)存資源重新使用而避免去創(chuàng)建新的。最典型的使用就是**緩存(Cache)和池(Pool)饭入。
  • 緩存:
    一種是內(nèi)存緩存嵌器,一種是硬盤緩存。
    • 內(nèi)存緩存(LruCache):存在寶貴的內(nèi)存中谐丢。
    • 硬盤緩存(DiskLruCache):存在硬盤中爽航。
  • 池:
    一種是對象池,一種是線程池乾忱。
    • 對象池:將用過的對象保存起來讥珍,等下一次使用時,再拿出來使用窄瘟, 從而在一定情況下減少創(chuàng)造對象所產(chǎn)生的開銷衷佃。但是不是任何對象都適合使用對象池,因為維護對象池也需要一定的開支蹄葱。一定要“維護對象池的開支”小于“創(chuàng)建對象的開支”氏义。
    • 線程池:基本思想任然是對象池的思想,在開辟的一片空間里图云,存著很多線程惯悠,線程池的調(diào)度由池管理器來處理。當需要一個線程時竣况,從池中取一個克婶,用完后重新歸池,減少了創(chuàng)建線程的開支丹泉。
      java提供了ExecutorServiceExecutors類情萤,我們可以用它們?nèi)?chuàng)建線程池。

6.優(yōu)化——回收不需要的內(nèi)存

  • 回收主要是Dalvik的GC機制摹恨。下面來講一些具體的:

  • 線程回收:
    線程中涉及的任何東西GC都不能回收筋岛,所以線程很容易造成內(nèi)存泄露。

Thread t = new Thread() {  
    public void run() {  
        while (true) {  
            try {  
                Thread.sleep(1000);  
                System.out.println("thread is running...");  
            } catch (InterruptedException e) {             
            }  
        }  
    }  
};  
t.start();  
t = null;  
System.gc();  

這個時候GC并不會回收這個線程睬塌,因為正在運行的線程屬于GCRoot的一種,并不會被GC進行回收。

  • 游標回收:
    Cursor是Android查詢數(shù)據(jù)后得到的一個管理數(shù)據(jù)集合的類揩晴,應(yīng)該在使用完之后立即手動進行關(guān)閉勋陪。一般使用如下:
Cursor cursor = null;  
try {  
    cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
    if(cursor != null) {  
        cursor.moveToFirst();  
    }  
} catch (Exception e) {  
    e.printStackTrace();  
} finally {  
    if (cursor != null) {  
        cursor.close();  
    }  
}  

:在CursorAdapter中應(yīng)用時,我們不能直接關(guān)掉Cursor硫兰,而且CursorAdapter在Acivity結(jié)束時并沒有自動的將Cursor關(guān)閉掉诅愚,因此,需要在onDestroy函數(shù)中劫映,手動關(guān)閉违孝。

@Override    
protected void onDestroy() {          
    if (mAdapter != null && mAdapter.getCurosr() != null) {    
        mAdapter.getCursor().close();    
    }    
    super.onDestroy();     
}    
  • Receiver(接收器)回收
    當在Activity進行了Receiver注冊時,需要注意最后要注銷泳赋。
    即調(diào)用registerReceiver()雌桑,使用完畢后調(diào)用unregisterReceiver()。
    一般在onDestroy()進行回收祖今。
@Override    
protected void onDestroy() {    
      this.unregisterReceiver(receiver);    
      super.onDestroy();    
}    
  • Stream/File(流/文件)回收:
    各種流/文件如:
    InputStream/OutputStream校坑,SQListeOpenHelper,SQLiteDatabase千诬,Cursor耍目,F(xiàn)ile,I/O徐绑,Bitmap都需要關(guān)閉邪驮。

7.優(yōu)化——視圖布局優(yōu)化

  • 使用HierarchyViewer查看并減少OverDraw。
  • ViewStub標簽:可以使布局在可見與不可見之間切換傲茄,默認不可見的情況下不占用內(nèi)存毅访。
  • include標簽:復用UI資源。
  • merge標簽:解決include標簽的問題烫幕,減少一個層級俺抽,并且方便使用。
  • 重用系統(tǒng)資源:使用系統(tǒng)自帶的資源(sdk\platforms\android-x\data\res)较曼,例如:
    • id
      android:id="@android:id/list"
    • 字符串
      @android:string/yes
    • Style
      android:textAppearance="?android:attr/textAppearanceMedium"
    • 顏色
      android:background ="@android:color/transparent"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磷斧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捷犹,更是在濱河造成了極大的恐慌弛饭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍歉,死亡現(xiàn)場離奇詭異侣颂,居然都是意外死亡,警方通過查閱死者的電腦和手機枪孩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門憔晒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藻肄,“玉大人,你說我怎么就攤上這事拒担∴谕停” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵从撼,是天一觀的道長州弟。 經(jīng)常有香客問我,道長低零,這世上最難降的妖魔是什么婆翔? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮掏婶,結(jié)果婚禮上啃奴,老公的妹妹穿的比我還像新娘。我一直安慰自己气堕,他們只是感情好纺腊,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著茎芭,像睡著了一般揖膜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梅桩,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天壹粟,我揣著相機與錄音,去河邊找鬼宿百。 笑死趁仙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的垦页。 我是一名探鬼主播雀费,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼痊焊!你這毒婦竟也來了盏袄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤薄啥,失蹤者是張志新(化名)和其女友劉穎辕羽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垄惧,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡刁愿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了到逊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铣口。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡滤钱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脑题,到底是詐尸還是另有隱情菩暗,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布旭蠕,位于F島的核電站,受9級特大地震影響旷坦,放射性物質(zhì)發(fā)生泄漏掏熬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一秒梅、第九天 我趴在偏房一處隱蔽的房頂上張望旗芬。 院中可真熱鬧,春花似錦捆蜀、人聲如沸疮丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽誊薄。三九已至,卻和暖如春锰茉,著一層夾襖步出監(jiān)牢的瞬間呢蔫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工飒筑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留片吊,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓协屡,卻偏偏與公主長得像俏脊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肤晓,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容