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)上,長期保持一些資源的引用,造成一些內(nèi)存不能釋放,帶來的內(nèi)存泄露問題很多。比如:Context(下文中提到的Activity都是Context),在一些你需要保持你的首個(gè)類對象狀態(tài),并且把狀態(tài)傳入其他類對象中時(shí),這樣消除掉首個(gè)類對象之前,你必須先把接收類對象釋放掉。需要注意一點(diǎn)的是:因?yàn)樵贘ava或者Android內(nèi)存機(jī)制中,頂點(diǎn)的結(jié)點(diǎn)釋放前必須保證其他對象沒有調(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,就必須先釋放掉引用他的一些對象。如果沒有,那在要釋放Context的時(shí)候,你會(huì)發(fā)現(xiàn)會(huì)有大量的內(nèi)存溢出。所以在你不小心的情況下內(nèi)存溢出是一件非常容易的事情。 保存一些對象時(shí),同時(shí)也會(huì)造成內(nèi)存泄露。最簡單的比如說位圖(Bitmap),比如說:在屏幕旋轉(zhuǎn)時(shí),會(huì)破壞當(dāng)前保持的一個(gè)Activity狀態(tài),并且重新申請生成新的Activity,直到新的Activity狀態(tài)被保存。我們再看一段代碼:
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 } } }
代碼很簡單,但是在Android上又來新問題了,當(dāng)我們在切換視圖屏幕的時(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對象的生命周期是不確定的,是應(yīng)用程序無法控制的,因此如果AsyncTask作為Activity的內(nèi)部類,就更容易出現(xiàn)內(nèi)存泄露的問題。
線程問題的改進(jìn)方式主要有:
① 將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。
② 在程序中盡量采用弱引用保存Context。
2. 萬惡的bitmap。。。
Bitmap是一個(gè)很萬惡的對象,對于一個(gè)內(nèi)存對象,如果該對象所占內(nèi)存過大,在超出了系統(tǒng)的內(nèi)存限制時(shí)候,內(nèi)存泄露問題就很明顯了。。
解決bitmap主要是要解決在內(nèi)存盡量不保存它或者使得采樣率變小。在很多場合下,因?yàn)槲覀兊膱D片像素很高,而對于手機(jī)屏幕尺寸來說我們并不用那么高像素比例的圖片來加載時(shí),我們就可以先把圖片的采樣率降低在做原來的UI操作。
如果在我們不需要保存bitmap對象的引用時(shí)候,我們還可以用軟引用來做替換。具體的實(shí)例代碼google上面也有很多。
綜上所述,要避免內(nèi)存泄露,主要要遵循以下幾點(diǎn):
第一:不要為Context長期保存引用(要引用Context就要使得引用對象和它本身的生命周期保持一致)。
第二:如果要使用到Context,盡量使用ApplicationContext去代替Context,因?yàn)锳pplicationContext的生命周期較長,引用情況下不會(huì)造成內(nèi)存泄露問題
第三:在你不控制對象的生命周期的情況下避免在你的Activity中使用static變量。盡量使用WeakReference去代替一個(gè)static。
第四:垃圾回收器并不保證能準(zhǔn)確回收內(nèi)存,這樣在使用自己需要的內(nèi)容時(shí),主要生命周期和及時(shí)釋放掉不需要的對象。盡量在Activity的生命周期結(jié)束時(shí),在onDestroy中把我們做引用的其他對象做釋放,比如:cursor.close()。
其實(shí)我們可以在很多方面使用更少的代碼去完成程序。比如:我們可以多的使用9patch圖片等。有很多細(xì)節(jié)地方都可以值得我們?nèi)グl(fā)現(xiàn)、挖掘更多的內(nèi)存問題。我們要是能做到C/C++對于程序的“誰創(chuàng)建,誰釋放”原則,那我們對于內(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é)》
希望本文所述對大家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自定義控件ImageView實(shí)現(xiàn)點(diǎn)擊之后出現(xiàn)陰影效果
這篇文章主要為大家詳細(xì)介紹了Android自定義控件ImageView實(shí)現(xiàn)點(diǎn)擊之后有陰影效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android編程實(shí)現(xiàn)WebView自適應(yīng)全屏方法小結(jié)
這篇文章主要介紹了Android編程實(shí)現(xiàn)WebView自適應(yīng)全屏方法,結(jié)合實(shí)例形式總結(jié)了三種常用的WebView自適應(yīng)全屏實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12制作獨(dú)立的Android模擬器實(shí)現(xiàn)方法
本文主要介紹如何制作獨(dú)立的Android模擬器,這里給大家提供詳細(xì)的制作流程,有需要的小伙伴可以參考下2016-08-08Fragment跳轉(zhuǎn)時(shí)傳遞參數(shù)及結(jié)果回傳的方法(推薦)
今天總結(jié)一下Fragment間的參數(shù)傳遞及結(jié)果返回的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01Android開發(fā)InputManagerService創(chuàng)建與啟動(dòng)流程
這篇文章主要為大家介紹了Android開發(fā)InputManagerService創(chuàng)建與啟動(dòng)流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Ubuntu16.04 LTS 下安裝 Android Studio 2.2.2 的詳細(xì)步驟
這篇文章主要介紹了Ubuntu16.04 LTS 下安裝 Android Studio 2.2.2 的詳細(xì)步驟,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Android ViewDragHelper完全解析 自定義ViewGroup神器
這篇文章主要為大家詳細(xì)介紹了Android ViewDragHelper完全解析,自定義ViewGroup神器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03