Android記憶體優化 1
APP記憶體管理
App切換用的是LRU cache
onTrimMemory(level)
如果記憶體不夠的時候系統會發送請求,各個app會回撥這個函式,然後app會把自己不用的記憶體釋放掉,這樣下次系統啟動其他app的時候,如果你這個app佔用系統記憶體小,可能就不清除你
動態的觀察記憶體變化情況:
可以在app中切換activity,然後看記憶體時而變多時而變少。如果一直不斷佔用變多,說名可能有記憶體洩漏
動態檢視記憶體變化的方法
- 在程式碼中看,通過程式碼,不停輸出記憶體佔用情況
total:已經分配了11M
free:3M空閒,值得是該用目前一共用了14M,3M已經被釋放了,空閒著的,
max:給改應用分配的最大記憶體是384M
2.profile監控
https://developer.android.google.cn/studio/profile/memory-profiler
第三種方式:
如果data object 或者classobejct浮動在一個大小區間就是正常的,如果不斷變大 說明有記憶體洩漏
App記憶體優化方法
資料結構優化
string物件會產生很多中間變數,gc會回收他,但是如果很多的話,英文gc也是需要記憶體的,所以效率會很低
使用ArrayMap,SparseArray代替HashMap
記憶體抖動
程式碼設計的時候,變數使用不當造成的。
比如突然申請了很多變數,但是很快又不用來,突然有申請了很多,如果這時候heap記憶體 不夠了,gc就會對原來不用的記憶體進行回收,gc在回收的時候
會把所有執行緒都暫停,這個過程如果在比較短的時間內重複出現。比如申請了額很多物件變數或者空間,用了一小會有不用,然後又申請很多,又不用,如果 gc回收的時候佔用的時間多,就會造成卡頓,使用者體驗差
上面程式碼,反覆建立strMatrix,然後在第二層迴圈裡賦值,然後回到第一層迴圈,又不用剛剛的變量了,又重新申請一個strMatrx陣列,然後在賦值,。。
解決很簡單,只要把strMatrix放在for迴圈外面這樣,就不會反覆建立strmatrix陣列了
再小的Class也要耗費0.5kb
不要毫無節制的建立新的Class
物件複用
複用系統自帶的資源
listview的convertView的複用
避免在onDraw方法裡執行物件的建立
因為ondraw裡物件如果有變化,的話就會重新執行onDraw方法,這樣會影響onDraw繪製時間,而且物件建立也會反覆執行,可能會有卡頓
避免記憶體洩漏
如果這樣的情況發生嚴重,那麼heap會越來越小,gc會頻繁回收,但是又回收不了。這時候可能會導致OOM崩潰,也就是達到最大記憶體使用的限度
一個例子
一個按鈕,點選後開一個執行緒
執行緒沒結束前,會一直引用這個activity,導致activity不能被回收。
點選start,開啟一個執行緒
多進入幾次acitvity點選按鈕,導致各個執行緒都持有一個activity,記憶體佔用會越來越大,
一直到執行緒結束,activity才會被回收。
TestThread引用activity,所以,執行緒開的越多,因為持續時間長,持有的activity就越多,導致記憶體洩漏。
與上面例子相同的例子
//MainActivity.java void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start(); } View tButton = findViewById(R.id.t_button); tButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { spawnThread(); nextActivity();//進入下一個activity,但是執行緒還沒結束,會持有這個activity的引用 } });
怎麼解決?
首先會想到執行緒設定為靜態類,比如使用靜態的Thread或者AsyncTask。
那我們自定義Thread並宣告成static這樣可以嗎?其實這樣的做法並不推薦,因為Thread位於GC根部,DVM會和所有的活動執行緒保持hard references關係,所以執行中的Thread絕不會被GC無端回收了,所以正確的解決辦法是在自定義靜態內部類的基礎上給執行緒加上取消機制,因此我們可以在Activity的onDestroy方法中將thread關閉掉。
放在service裡取資料,IntentService?
activity很容易洩漏
使用activity context引用上下文
解決辦法:使用Application Context。因為Application 在應用開的時候,一直存在,而activity經常切換,如果持有切換掉的activity,就導致記憶體洩漏了。
注意Cursor物件是否及時關閉
資料庫物件的時候需要注意
演示
為什麼需要在TypedArray後呼叫recycle
當我們沒有在使用TypedArray後呼叫recycle,編譯器會提示“This TypedArray should be recycled after use with #recycle()”。
官方的解釋是:回收TypedArray,以便後面重用。在呼叫這個函式後,你就不能再使用這個TypedArray。
在TypedArray後呼叫recycle主要是為了快取。當recycle被呼叫後,這就說明這個物件從現在可以被重用了。TypedArray
內部持有部分陣列,它們快取在Resources類中的靜態欄位中,這樣就不用每次使用前都需要分配記憶體。你可以看看
TypedArray.recycle()
中的程式碼: