Android編程內(nèi)存溢出與防范方法淺析
本文實(shí)例講述了Android編程內(nèi)存溢出與防范方法。分享給大家供大家參考,具體如下:
Android的虛擬機(jī)是基于寄存器的Dalvik,它的最大堆大小一般是16M。但是Android采用的是Java語言編寫,所以在很大程度上,Android的內(nèi)存機(jī)制等同于Java的內(nèi)存機(jī)制,在剛開始開發(fā)的時(shí)候,內(nèi)存的限制問題會(huì)給我們帶來內(nèi)存溢出等嚴(yán)重問題。在我們不使用一些內(nèi)存的時(shí)候,我們要盡量在Android或者其他平臺(tái)上避免在運(yùn)行其他程序時(shí),保存必要的狀態(tài),使得一些死進(jìn)程所帶來的內(nèi)存問題,應(yīng)該盡量在關(guān)閉程序或者保存狀態(tài)的時(shí)候釋放掉,這樣能提高系統(tǒng)在運(yùn)行方面的流暢性。
Android的內(nèi)存主要表現(xiàn)在:
1. 在Android平臺(tái)上,長(zhǎng)期保持一些資源的引用,造成一些內(nèi)存不能釋放,帶來的內(nèi)存泄露問題很多。比如:Context(下文中提到的Activity都是Context),在一些你需要保持你的首個(gè)類對(duì)象狀態(tài),并且把狀態(tài)傳入其他類對(duì)象中時(shí),這樣消除掉首個(gè)類對(duì)象之前,你必須先把接收類對(duì)象釋放掉。需要注意一點(diǎn)的是:因?yàn)樵贘ava或者Android內(nèi)存機(jī)制中,頂點(diǎn)的結(jié)點(diǎn)釋放前必須保證其他對(duì)象沒有調(diào)用才能被系統(tǒng)GC回收釋放。我們來看一段代碼:
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextViewlabel = new TextView(this);
label.setText("Leaksare bad");
setContentView(label);
}
這個(gè)代碼的意思就是我們把一個(gè)TextView的實(shí)例加載到了我們正在運(yùn)行的Activity(Context)當(dāng)中,因此,通過GC回收機(jī)制,我們知道,要釋放Context,就必須先釋放掉引用他的一些對(duì)象。如果沒有,那在要釋放Context的時(shí)候,你會(huì)發(fā)現(xiàn)會(huì)有大量的內(nèi)存溢出。所以在你不小心的情況下內(nèi)存溢出是一件非常容易的事情。 保存一些對(duì)象時(shí),同時(shí)也會(huì)造成內(nèi)存泄露。最簡(jiǎn)單的比如說位圖(Bitmap),比如說:在屏幕旋轉(zhuǎn)時(shí),會(huì)破壞當(dāng)前保持的一個(gè)Activity狀態(tài),并且重新申請(qǐng)生成新的Activity,直到新的Activity狀態(tài)被保存。我們?cè)倏匆欢未a:
privatestatic Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground =getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
這個(gè)代碼是非??斓耐瑫r(shí)也是錯(cuò)誤的。它的內(nèi)存泄露很容易出在屏幕轉(zhuǎn)移的方向上。雖然我們會(huì)發(fā)現(xiàn)沒有顯示的保存Context這個(gè)實(shí)例,但是當(dāng)我們把繪制的圖連接到一個(gè)視圖的時(shí)候,Drawable就會(huì)將被View設(shè)置為回調(diào),這就說明,在上述的代碼中,其實(shí)在繪制TextView到活動(dòng)中的時(shí)候,我們已經(jīng)引用到了這個(gè)Activity。鏈接情況可以表現(xiàn)為:Drawable->TextView->Context。
所以在想要釋放Context的時(shí)候,其實(shí)還是保存在內(nèi)存中,并沒有得到釋放。
如何避免這種情況:主要在于。線程最容易出錯(cuò)。大家不要小看線程,在Android里面線程最容易造成內(nèi)存泄露。線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控。下面有一段代碼:
publicclass MyTest extends Activity {
@Override
publicvoid onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
}
privateclass MyThread extends Thread{
@Override
public void run() {
super.run();
//do somthing
}
}
}
代碼很簡(jiǎn)單,但是在Android上又來新問題了,當(dāng)我們?cè)谇袚Q視圖屏幕的時(shí)候(橫豎屏),就會(huì)重新建立橫屏或者豎屏的Activity。我們形象的認(rèn)為之前建立的Activity會(huì)被回收,但是事實(shí)如何呢?Java機(jī)制不會(huì)給你同樣的感受,在我們釋放Activity之前,因?yàn)閞un函數(shù)沒有結(jié)束,這樣MyThread并沒有銷毀,因此引用它的Activity(Mytest)也有沒有被銷毀,因此也帶來的內(nèi)存泄露問題。
有些人喜歡用Android提供的AsyncTask,但事實(shí)上AsyncTask的問題更加嚴(yán)重,Thread只有在run函數(shù)不結(jié)束時(shí)才出現(xiàn)這種內(nèi)存泄露問題,然而AsyncTask內(nèi)部的實(shí)現(xiàn)機(jī)制是運(yùn)用了ThreadPoolExcutor,該類產(chǎn)生的Thread對(duì)象的生命周期是不確定的,是應(yīng)用程序無法控制的,因此如果AsyncTask作為Activity的內(nèi)部類,就更容易出現(xiàn)內(nèi)存泄露的問題。
線程問題的改進(jìn)方式主要有:
① 將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。
② 在程序中盡量采用弱引用保存Context。
2. 萬惡的bitmap。。。
Bitmap是一個(gè)很萬惡的對(duì)象,對(duì)于一個(gè)內(nèi)存對(duì)象,如果該對(duì)象所占內(nèi)存過大,在超出了系統(tǒng)的內(nèi)存限制時(shí)候,內(nèi)存泄露問題就很明顯了。。
解決bitmap主要是要解決在內(nèi)存盡量不保存它或者使得采樣率變小。在很多場(chǎng)合下,因?yàn)槲覀兊膱D片像素很高,而對(duì)于手機(jī)屏幕尺寸來說我們并不用那么高像素比例的圖片來加載時(shí),我們就可以先把圖片的采樣率降低在做原來的UI操作。
如果在我們不需要保存bitmap對(duì)象的引用時(shí)候,我們還可以用軟引用來做替換。具體的實(shí)例代碼google上面也有很多。
綜上所述,要避免內(nèi)存泄露,主要要遵循以下幾點(diǎn):
第一:不要為Context長(zhǎng)期保存引用(要引用Context就要使得引用對(duì)象和它本身的生命周期保持一致)。
第二:如果要使用到Context,盡量使用ApplicationContext去代替Context,因?yàn)锳pplicationContext的生命周期較長(zhǎng),引用情況下不會(huì)造成內(nèi)存泄露問題
第三:在你不控制對(duì)象的生命周期的情況下避免在你的Activity中使用static變量。盡量使用WeakReference去代替一個(gè)static。
第四:垃圾回收器并不保證能準(zhǔn)確回收內(nèi)存,這樣在使用自己需要的內(nèi)容時(shí),主要生命周期和及時(shí)釋放掉不需要的對(duì)象。盡量在Activity的生命周期結(jié)束時(shí),在onDestroy中把我們做引用的其他對(duì)象做釋放,比如:cursor.close()。
其實(shí)我們可以在很多方面使用更少的代碼去完成程序。比如:我們可以多的使用9patch圖片等。有很多細(xì)節(jié)地方都可以值得我們?nèi)グl(fā)現(xiàn)、挖掘更多的內(nèi)存問題。我們要是能做到C/C++對(duì)于程序的“誰創(chuàng)建,誰釋放”原則,那我們對(duì)于內(nèi)存的把握,并不比Java或Android本身的GC機(jī)制差,而且更好的控制內(nèi)存,能使我們的手機(jī)運(yùn)行得更流暢。
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android開發(fā)之內(nèi)存與緩存技巧總結(jié)》、《Android開發(fā)入門與進(jìn)階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android多媒體操作技巧匯總(音頻,視頻,錄音等)》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- Android 內(nèi)存溢出和內(nèi)存泄漏的問題
- Android避免內(nèi)存溢出(Out of Memory)方法匯總
- Android 使用幀動(dòng)畫內(nèi)存溢出解決方案
- android 解決ViewPager加載大量圖片內(nèi)存溢出問題
- Android編程之內(nèi)存溢出解決方案(OOM)實(shí)例總結(jié)
- Android加載圖片內(nèi)存溢出問題解決方法
- android內(nèi)存及內(nèi)存溢出分析詳解
- Android 異步獲取網(wǎng)絡(luò)圖片并處理導(dǎo)致內(nèi)存溢出問題解決方法
- Android中Memory Leak原因分析及解決辦法
相關(guān)文章
Android進(jìn)階之從IO到NIO的模型機(jī)制演進(jìn)
這篇文章主要為大家介紹了Android進(jìn)階之從IO到NIO的模型機(jī)制演進(jìn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
android listview進(jìn)階實(shí)例分享
這篇文章主要介紹了android listview進(jìn)階實(shí)例分享,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例
這篇文章主要介紹了Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例,本文給出了服務(wù)端代碼和客戶端代碼,需要的朋友可以參考下2014-10-10
android 使用okhttp可能引發(fā)OOM的一個(gè)點(diǎn)
這篇文章主要介紹了android 使用okhttp可能引發(fā)OOM的一個(gè)點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
Android7.0版本影響開發(fā)的改進(jìn)分析
這篇文章主要介紹了Android7.0版本影響開發(fā)的改進(jìn),總結(jié)分析了Android7.0版本中比較常見的開發(fā)注意事項(xiàng)與操作技巧,需要的朋友可以參考下2017-11-11
Android 判斷ip地址合法實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 判斷ip地址合法實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06

