標籤:
因為安卓是基於java語言的,所以我們先來看一看java中的記憶體流失,然後在此基礎上來談談安卓中的記憶體流失。
一java中的記憶體流失:
java中的記憶體流失主要是指在堆中分配的記憶體,明明已經不需要的時候,還仍然保留著訪問它的引用,導致GC回收不能及時回收(關於GC回收不做過多贅述),導致這種情況出現的最主要原因是長生命週期的對象持有短生命週期對象的引用,導致短生命週期的對象明明已經不需要卻無法被GC回收,從而導致記憶體流失。主要包括以下幾種情況:
1在一個類中建立了一個非靜態內部類的靜態執行個體,如下所示:
public class Outer{ static Inner inner= null; void doSomething() {//do something } class Inner { void doSomething() { System.out.println("dosth."); } } } 在上述代碼中,在第一次建立外部類Outer的執行個體時,會自動載入內部類inner(因為被static修飾),而內部類Inner持有外部類Outer的引用,且內部類inner的生命週期顯然和整個程式的生命週期相同(被static修飾),這樣當不需要使用外部類時,因為inner持有outer的引用,導致outer不能被GC回收,導致記憶體流失。
2使用java中的集合架構添加的對象在不需使用時未清除
如果我們把一些對象的引用加入到了集合中,當我們不需要該對象時,如果沒有把它的引用從集合中清理掉,而僅僅是將其用用賦值為null,讓GC去回收(而事實上GC是不會回收的),代碼如下:
Vector v = new Vector( 10 ); for ( int i = 1 ;i < 100 ; i ++ ){ Object o = new Object(); v.add(o); o = null ; }如在上述代碼中,我們在一個for迴圈中不斷產生新的Object對象,然後將其添加到集合Vector中,然後將該對象的引用置為空白(本意是置為null之後,讓GC去回收),但事實上GC是不會去回收的,因為雖然Object對象的引用被置空,但是仍然存在其它的引用路徑能夠找到該對象,這個引用是從Vector集合對象中找到的,即儘管 o 引用已經被置空,但是 Object 對象仍然存在其他的引用,是可以被訪問到的,所以 GC 無法將其釋放掉。如果在此迴圈之後, Object 對象對程式已經沒有任何作用,那麼我們就認為此 Java 程式發生了記憶體流失。
要解決此種情況的泄漏,需要在對象不需要使用時調用remove()方法將該對象從集合中清除掉。
3不合理的使用單例模式
其實此種情況與第一種情況非常類似,都是因為static關鍵字導致的記憶體流失。代碼如下:
//懶漢式單例類.在第一次調用的時候執行個體化自己 public class Singleton { private Singleton() {} private static Singleton single=null; public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; }}可以看到單例模式的類都是被static修飾的,因此該類的執行個體的生命週期與整個程式的生命週期相同,因此很容易造成記憶體流失。
4資料庫連接,網路連接(socket)和IO串連在不使用時未使用close()方法將串連關閉
這種情況只要注意在不需要使用資源時用close()方法將其關閉即可。
5修改hashset中對象的參數值,且參數是計算雜湊值的欄位:
這種情況與第二種情況非常類似,因為在第二種情況中我們已經討論過在使用java集合架構時避免記憶體流失的關鍵是當對象不需使用時將其從集合中remove掉。
但當一個對象被儲存進HashSet集合中以後,就不能修改這個對象中的那些參與計算雜湊值的欄位,否則對象修改後的雜湊值與最初儲存進HashSet集合中時的雜湊值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作為參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中刪除當前對象,造成記憶體泄露。
二安卓中的記憶體流失:
因為安卓是基於java語言的,所以很多情況與上述講述的java記憶體流失是相同的。
1)注意Activity的泄漏
通常來說,Activity的泄漏是記憶體流失裡面最嚴重的問題,它佔用的記憶體多,影響面廣,我們需要特別注意以下兩種情況導致的Activity泄漏:
內部類引用導致Activity的泄漏
最典型的情境是Handler導致的Activity泄漏,如果Handler中有延遲的任務或者是等待執行的任務隊列過長,都有可能因為Handler繼續執行而導致Activity發生泄漏。此時的參考關聯性鏈是Looper -> MessageQueue -> Message -> Handler -> Activity。為瞭解決這個問題,可以在UI退出之前,執行remove Handler訊息佇列中的訊息與runnable對象。或者是使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在參考關聯性的目的。
Activity Context被傳遞到其他執行個體中,這可能導致自身被引用而發生泄漏。
內部類引起的泄漏不僅僅會發生在Activity上,其他任何內部類出現的地方,都需要特別留意!我們可以考慮盡量使用static類型的內部類,同時使用WeakReference的機制來避免因為互相引用而出現的泄露。
2)考慮使用Application Context而不是Activity Context
對於大部分非必須使用Activity Context的情況(Dialog的Context就必須是Activity Context),我們都可以考慮使用Application Context而不是Activity的Context,這樣可以避免不經意的Activity泄露。
3)構造Adapter時,沒有使用緩衝的convertView
以構造ListView的BaseAdapter為例,在BaseAdapter中提供了方法:
public View getView(int position, ViewconvertView, ViewGroup parent)
來向ListView提供每一個item所需要的view對象。初始時ListView會從BaseAdapter中根據當前的螢幕布局執行個體化一定數量的 view對象,同時ListView會將這些view對象緩衝起來。當向上滾動ListView時,原先位於最上面的list item的view對象會被回收,然後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參View convertView就是被緩衝起來的list item的view對象(初始化時緩衝中沒有view對象則convertView是null)。由此可以看出,如果我們不去使用 convertView,而是每次都在getView()中重新執行個體化一個View對象的話,會白白浪費記憶體資源,會使得記憶體佔用越來越大。
4)注意監聽器的登出
在Android程式裡面存在很多需要register與unregister的監聽器,我們需要確保在合適的時候及時unregister那些監聽器。自己手動add的listener,需要記得及時remove這個listener。
5).資來源物件沒關閉造成的記憶體流失
資源性對象比如(Cursor,File檔案等)往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時回收記憶體。它們的緩衝不僅存在於 java虛擬機器內,還存在於java虛擬機器外。如果我們僅僅是把它的引用設定為null,而不關閉它們,往往會造成記憶體流失。
因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置為null.在我們的程式退出時一定要確保我們的資源性對象已經關閉。
以下是錯誤碼及正確代碼的示範:
Cursor cursor = getContentResolver().query(uri...); if (cursor.moveToNext()) { ... ... }正確代碼:Cursor cursor = null; try { cursor = getContentResolver().query(uri...); if (cursor != null &&cursor.moveToNext()) { ... ... } } finally { if (cursor != null) { try { cursor.close(); } catch (Exception e) { //ignore this } } }
Bitmap對象不在使用時調用recycle()釋放記憶體
雖然在大多數情況下,我們會對Bitmap增加緩衝機制,但是在某些時候,部分Bitmap是需要及時回收的。例如臨時建立的某個相對比較大的bitmap對象,在經過變換得到新的bitmap對象之後,應該調用Bitmap.recycle()方法儘快回收原始的bitmap,這樣能夠更快釋放原始bitmap所佔用的空間。
需要特別留意的是Bitmap類裡面提供的createBitmap()方法,:
這個函數返回的bitmap有可能和source bitmap是同一個,在回收的時候,需要特別檢查source bitmap與return bitmap的引用是否相同,只有在不等的情況下,才能夠執行source bitmap的recycle方法。
好了以上就是本人理解的關於安卓中記憶體流失的知識,另外推薦大家看一下:http://www.jb51.net/article/77899.htm,用代碼的形式詳細介紹了幾種記憶體流失與解決方案。如果大家覺得不錯記得頂一下哦。
安卓中的記憶體流失