Android內存泄漏(lou)
時間(jian):2018-09-27 來源:未知
Android 主要應用(yong)(yong)(yong)(yong)在(zai)嵌入式設備當中,而嵌入式設備由(you)于一些(xie)眾所周知(zhi)的(de)條件限(xian)(xian)制(zhi),通常都不(bu)會(hui)(hui)(hui)有(you)(you)很高的(de)配置,特別是(shi)(shi)內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)是(shi)(shi)比較有(you)(you)限(xian)(xian)的(de)。如(ru)果(guo)我(wo)們(men)編寫的(de)代(dai)碼(ma)當中有(you)(you)太多的(de)對內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)使(shi)用(yong)(yong)(yong)(yong)不(bu)當的(de)地方(fang),難免會(hui)(hui)(hui)使(shi)得(de)我(wo)們(men)的(de)設備運(yun)(yun)(yun)行緩(huan)慢,甚至是(shi)(shi)死機(ji)。為了能夠使(shi)得(de)Android應用(yong)(yong)(yong)(yong)程(cheng)(cheng)(cheng)(cheng)序(xu)安全且快速的(de)運(yun)(yun)(yun)行,Android的(de)每個(ge)應用(yong)(yong)(yong)(yong)程(cheng)(cheng)(cheng)(cheng)序(xu)都會(hui)(hui)(hui)使(shi)用(yong)(yong)(yong)(yong)一個(ge)專(zhuan)有(you)(you)的(de)Dalvik虛(xu)擬機(ji)實例來運(yun)(yun)(yun)行,它是(shi)(shi)由(you)Zygote服務進(jin)程(cheng)(cheng)(cheng)(cheng)孵化出(chu)來的(de),也就是(shi)(shi)說每個(ge)應用(yong)(yong)(yong)(yong)程(cheng)(cheng)(cheng)(cheng)序(xu)都是(shi)(shi)在(zai)屬于自(zi)己的(de)進(jin)程(cheng)(cheng)(cheng)(cheng)中運(yun)(yun)(yun)行的(de)。一方(fang)面,如(ru)果(guo)程(cheng)(cheng)(cheng)(cheng)序(xu)在(zai)運(yun)(yun)(yun)行過(guo)程(cheng)(cheng)(cheng)(cheng)中出(chu)現了內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)泄漏(lou)的(de)問題(ti),僅僅會(hui)(hui)(hui)使(shi)得(de)自(zi)己的(de)進(jin)程(cheng)(cheng)(cheng)(cheng)被(bei)(bei)kill掉,而不(bu)會(hui)(hui)(hui)影響其他進(jin)程(cheng)(cheng)(cheng)(cheng)(如(ru)果(guo)是(shi)(shi)system_process等系統(tong)進(jin)程(cheng)(cheng)(cheng)(cheng)出(chu)問題(ti)的(de)話,則會(hui)(hui)(hui)引(yin)起系統(tong)重(zhong)啟)。另一方(fang)面Android為不(bu)同(tong)類型的(de)進(jin)程(cheng)(cheng)(cheng)(cheng)分配了不(bu)同(tong)的(de)內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)使(shi)用(yong)(yong)(yong)(yong)上(shang)限(xian)(xian),如(ru)果(guo)應用(yong)(yong)(yong)(yong)進(jin)程(cheng)(cheng)(cheng)(cheng)使(shi)用(yong)(yong)(yong)(yong)的(de)內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)超(chao)過(guo)了這個(ge)上(shang)限(xian)(xian),則會(hui)(hui)(hui)被(bei)(bei)系統(tong)視為內(nei)(nei)(nei)(nei)存(cun)(cun)(cun)泄漏(lou),從而被(bei)(bei)kill掉。
本文引用地址://fsbing.cn/emb/Column/7558.html
以下四個方面會引起內存泄露, 我們在代碼編寫的過程中要注意:
1. 查詢數據庫后游標沒有關閉
Cursor cursor = getContentResolver().query(uri...);
if (cursor.moveToNext()){
......
}
修改后的代碼(ma):
Cursor cursor = null;
try{
cursor = getContentResolver().query(uri...);
if (cursor !=null && cursor.moveToNext()){
....
}//www.sctarena.com
} finally {
if (cursor != null){
try {
cursor.close();
} catch (Exception e){
}
}
}
2. 在構造adapter時, 沒有使用緩存的convertView
以(yi)構造ListView的(de)(de)BaseAdapter為(wei)例(li),public View getView(intposition, View convertView, ViewGroup parent)來(lai)(lai)向(xiang)ListView 提供每一個(ge)item 所(suo)需要的(de)(de)view 對(dui)象(xiang)(xiang)。初始時ListView 會從BaseAdapter 中根據當前的(de)(de)屏幕布(bu)局實例(li)化一定數量(liang)的(de)(de)view 對(dui)象(xiang)(xiang),同時ListView 會將(jiang)這些view 對(dui)象(xiang)(xiang)緩(huan)存(cun)起(qi)來(lai)(lai)。當向(xiang)上(shang)滾(gun)動ListView 時,原先位于上(shang)面(mian)的(de)(de)list item 的(de)(de)view 對(dui)象(xiang)(xiang)會被(bei)回收(shou),然后被(bei)用來(lai)(lai)構造新(xin)出現的(de)(de)下面(mian)的(de)(de)listitem。這個(ge)構造過程就(jiu)(jiu)是由getView()方法完成的(de)(de),getView()的(de)(de)第二個(ge)形參ViewconvertView 就(jiu)(jiu)是被(bei)緩(huan)存(cun)起(qi)來(lai)(lai)的(de)(de)list item 的(de)(de)view對(dui)象(xiang)(xiang)(初始化時緩(huan)存(cun)中沒有view對(dui)象(xiang)(xiang)則convertView是null)。
由此可以看出,如果我們不去使(shi)用convertView,而是每次都在(zai)getView()中重(zhong)新實例化一個View對象(xiang)的(de)話,即(ji)浪(lang)費(fei)資源也浪(lang)費(fei)時間,也會使(shi)得(de)內存(cun)占用越(yue)來越(yue)大。ListView回(hui)收listitem的(de)view對象(xiang)的(de)過程可以查看:
public View getView(int position, View convertView, ViewGroup parent){
View view = new XXXView();
......
return view;
}
修改后(hou)的(de)代(dai)碼:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
...
} else {
view = new Xxx(...);
...
}
return view;
}
3. Bitmap對象不再使用時, 沒有調用recycle()釋放內存
有時(shi)我們會手工的操作Bitmap對(dui)象,如果一個(ge)Bitmap對(dui)象比較占內(nei)存,當它(ta)不在被使(shi)用(yong)的時(shi)候(hou),可以(yi)調用(yong)Bitmap.recycle()方法回收此對(dui)象的像素所占用(yong)的內(nei)存,但這不是(shi)必(bi)須的,視情況而定。可以(yi)看(kan)一下(xia)代碼中的注(zhu)釋(shi):
/**
* Free up the memory associated with this bitmap’s pixels, and mark the
* bitmap as "dead", meaning it will throw an exception ifgetPixels() or
* setPixels() is called, and will draw nothing. This operation cannot be
* reversed, so it should only be called if you are sure there are no
* further uses for the bitmap. This is an advanced call, and normally need
* not be called, since the normal GC process will free up this memory when
* there are no more references to this bitmap.
*/
4. 釋放對象的引用
public DemoActivity extends Activity{
private Handler mHandler = new Handler();
private Object obj;
public void operation() {
obj = initObj();
[Mark]
mHandler.post(new Runnable() {
public void run() {
useObj(obj);
}
});
}
}
我們有一個(ge)成員變量(liang)obj,在(zai)(zai)operation()中我們希(xi)望(wang)能(neng)夠將(jiang)處理(li)obj 實例的(de)操作post 到某個(ge)線程的(de)MessageQueue 中。在(zai)(zai)以上的(de)代(dai)碼(ma)中,即便是mHandler 所在(zai)(zai)的(de)線程使(shi)用(yong)(yong)完了obj所引用(yong)(yong)的(de)對(dui)(dui)(dui)象(xiang)(xiang),但這個(ge)對(dui)(dui)(dui)象(xiang)(xiang)仍然不會(hui)被垃圾回收掉,因為(wei)DemoActivity.obj 還(huan)保有這個(ge)對(dui)(dui)(dui)象(xiang)(xiang)的(de)引用(yong)(yong)。所以如果在(zai)(zai)DemoActivity 中不再使(shi)用(yong)(yong)這個(ge)對(dui)(dui)(dui)象(xiang)(xiang)了,可以在(zai)(zai)[Mark]的(de)位置釋放對(dui)(dui)(dui)象(xiang)(xiang)的(de)引用(yong)(yong),而代(dai)碼(ma)可以修改為(wei):
public void operation() {
obj = initObj();
...
final Object o = obj; // o引用在這(zhe)個(ge)函數結束就會(hui)釋放(fang)掉(diao)
obj = null; // 這個地方(fang)釋放引用(yong)
mHandler.post(new Runnable() {
public void run() {
useObj(o);
}
}
}
假(jia)設我(wo)們(men)希望(wang)在(zai)鎖(suo)屏界面(LockScreen)中(zhong),監聽系統中(zhong)的電(dian)話服(fu)務(wu)以(yi)獲取一(yi)些信息(如(ru)信號強度等),則可以(yi)在(zai)LockScreen 中(zhong)定義(yi)一(yi)個PhoneStateListener 的對象(xiang),同時(shi)將它注(zhu)冊到TelephonyManager 服(fu)務(wu)中(zhong)。對于(yu)LockScreen 對象(xiang),當需要顯示鎖(suo)屏界面的時(shi)候就會創建(jian)一(yi)個LockScreen 對象(xiang),而當鎖(suo)屏界面消失的時(shi)候LockScreen 對象(xiang)就會被釋放掉。
但是如果(guo)在釋放(fang)LockScreen 對(dui)象(xiang)(xiang)(xiang)的(de)(de)時(shi)候(hou)忘記取消我們(men)之前注冊(ce)的(de)(de)PhoneStateListener 對(dui)象(xiang)(xiang)(xiang),則會導致LockScreen 無法(fa)被(bei)垃圾回(hui)收。如果(guo)不斷(duan)的(de)(de)使鎖屏界面顯(xian)示和消失,則終會由(you)于大量的(de)(de)LockScreen 對(dui)象(xiang)(xiang)(xiang)沒有辦法(fa)被(bei)回(hui)收而引(yin)起OutOfMemory,使得system_process 進程掛掉。總之當一個(ge)生命(ming)周(zhou)期(qi)較短(duan)的(de)(de)對(dui)象(xiang)(xiang)(xiang)A,被(bei)一個(ge)生命(ming)周(zhou)期(qi)較長的(de)(de)對(dui)象(xiang)(xiang)(xiang)B 保有其引(yin)用的(de)(de)情況下,在A 的(de)(de)生命(ming)周(zhou)期(qi)結束時(shi),要在B 中清除掉對(dui)A 的(de)(de)引(yin)用。
5. 其他情況
Android 應用程序中(zhong)典型(xing)的(de)需要(yao)注意釋放(fang)資(zi)(zi)源的(de)情(qing)(qing)況是在Activity 的(de)生(sheng)命周期中(zhong),在onPause()、onStop()、onDestroy()方(fang)法中(zhong)需要(yao)適當(dang)的(de)釋放(fang)資(zi)(zi)源的(de)情(qing)(qing)況。由于此情(qing)(qing)況很基(ji)礎,在此不詳細說明,具(ju)體(ti)可以查看官方(fang)文檔對Activity 生(sheng)命周期的(de)介紹,以明確何時應該釋放(fang)哪些資(zi)(zi)源。