Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [android開發]Android防止內存溢出淺析

[android開發]Android防止內存溢出淺析

編輯:關於Android編程

最近項目做得差不多了,測試出現了一些問題,其中一個就是內存溢出問題,在三星手機上測試最容易出現內存溢出,在其他手機上,比如華為就沒有發生,也是比較郁悶。這個問題在之前的公司,做項目時也遇到過,很大一方面是自己寫的代碼問題,今天在網上找了一些知識,給大家分享一下:

 

Android的虛擬機是基於寄存器的Dalvik,它的最大堆大小一般是16M。但是Android采用的是Java語言編寫,所以在很大程度上,Android的內存機制等同於Java的內存機制,在剛開始開發的時候,內存的限制問題會給我們帶來內存溢出等嚴重問題。在我們不使用一些內存的時候,我們要盡量在Android或者其他平台上避免在運行其他程序時,保存必要的狀態,使得一些死進程所帶來的內存問題,應該盡量在關閉程序或者保存狀態的時候釋放掉,這樣能提高系統在運行方面的流暢性。

Android的內存主要表現在:

1. 在Android平台上,長期保持一些資源的引用,造成一些內存不能釋放,帶來的內存洩露問題很多。比如:Context(下文中提到的Activity都是Context),在一些你需要保持你的首個類對象狀態,並且把狀態傳入其他類對象中時,這樣消除掉首個類對象之前,你必須先把接收類對象釋放掉。需要注意一點的是:因為在Java或者Android內存機制中,頂點的結點釋放前必須保證其他對象沒有調用才能被系統GC回收釋放。我們來看一段代碼:

 

 @Override  
 protected void onCreate(Bundle state) {  
      super.onCreate(state);  
      TextViewlabel = new TextView(this);  
      label.setText("Leaksare bad");  
      setContentView(label);  
 }  

 

這個代碼的意思就是我們把一個TextView的實例加載到了我們正在運行的Activity(Context)當中,因此,通過GC回收機制,我們知道,要釋放Context,就必須先釋放掉引用他的一些對象。如果沒有,那在要釋放Context的時候,你會發現會有大量的內存溢出。所以在你不小心的情況下內存溢出是一件非常容易的事情。 保存一些對象時,同時也會造成內存洩露。最簡單的比如說位圖(Bitmap),比如說:在屏幕旋轉時,會破壞當前保持的一個Activity狀態,並且重新申請生成新的Activity,直到新的Activity狀態被保存。我們再看一段代碼:

 

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);  
 }  

 

這個代碼是非常快的同時也是錯誤的。它的內存洩露很容易出在屏幕轉移的方向上。雖然我們會發現沒有顯示的保存Context這個實例,但是當我們把繪制的圖連接到一個視圖的時候,Drawable就會將被View設置為回調,這就說明,在上述的代碼中,其實在繪制TextView到活動中的時候,我們已經引用到了這個Activity。鏈接情況可以表現為:Drawable->TextView->Context。

所以在想要釋放Context的時候,其實還是保存在內存中,並沒有得到釋放。

如何避免這種情況:主要在於。線程最容易出錯。大家不要小看線程,在Android裡面線程最容易造成內存洩露。線程產生內存洩露的主要原因在於線程生命周期的不可控。下面有一段代碼:

 

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上又來新問題了,當我們在切換視圖屏幕的時候(橫豎屏),就會重新建立橫屏或者豎屏的Activity。我們形象的認為之前建立的Activity會被回收,但是事實如何呢?Java機制不會給你同樣的感受,在我們釋放Activity之前,因為run函數沒有結束,這樣MyThread並沒有銷毀,因此引用它的Activity(Mytest)也有沒有被銷毀,因此也帶來的內存洩露問題。

有些人喜歡用Android提供的AsyncTask,但事實上AsyncTask的問題更加嚴重,Thread只有在run函數不結束時才出現這種內存洩露問題,然而AsyncTask內部的實現機制是運用了ThreadPoolExcutor,該類產生的Thread對象的生命周期是不確定的,是應用程序無法控制的,因此如果AsyncTask作為Activity的內部類,就更容易出現內存洩露的問題。

線程問題的改進方式主要有:

l 將線程的內部類,改為靜態內部類。

l 在程序中盡量采用弱引用保存Context。

 

2. 萬惡的bitmap。。。

Bitmap是一個很萬惡的對象,對於一個內存對象,如果該對象所占內存過大,在超出了系統的內存限制時候,內存洩露問題就很明顯了。。

解決bitmap主要是要解決在內存盡量不保存它或者使得采樣率變小。在很多場合下,因為我們的圖片像素很高,而對於手機屏幕尺寸來說我們並不用那麼高像素比例的圖片來加載時,我們就可以先把圖片的采樣率降低在做原來的UI操作。

如果在我們不需要保存bitmap對象的引用時候,我們還可以用軟引用來做替換。具體的實例代碼google上面也有很多。

 

綜上所述,要避免內存洩露,主要要遵循以下幾點:

第一:不要為Context長期保存引用(要引用Context就要使得引用對象和它本身的生命周期保持一致)。

第二:如果要使用到Context,盡量使用ApplicationContext去代替Context,因為ApplicationContext的生命周期較長,引用情況下不會造成內存洩露問題

第三:在你不控制對象的生命周期的情況下避免在你的Activity中使用static變量。盡量使用WeakReference去代替一個static。

第四:垃圾回收器並不保證能准確回收內存,這樣在使用自己需要的內容時,主要生命周期和及時釋放掉不需要的對象。盡量在Activity的生命周期結束時,在onDestroy中把我們做引用的其他對象做釋放,比如:cursor.close()。

 

其實我們可以在很多方面使用更少的代碼去完成程序。比如:我們可以多的使用9patch圖片等。有很多細節地方都可以值得我們去發現、挖掘更多的內存問題。我們要是能做到C/C++對於程序的“誰創建,誰釋放”原則,那我們對於內存的把握,並不比Java或Android本身的GC機制差,而且更好的控制內存,能使我們的手機運行得更流暢。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved