Android 內存泄漏

jopen 10年前發布 | 17K 次閱讀 Android Android開發 移動開發

原文:http://android-developers.blogspot.jp/2009/01/avoiding-memory-leaks.html


Andriod應用的堆棧大小在T-Mobile G1上被限制在16MB,對于一個手機這已經是很大的內存了,但對一些開發者來說還是不夠。即使你不需要使用這么多的內存空間,你也應該盡可能地節省使用,這樣就不至于在其他應用運行的時候將你的殺掉。Android可以在內存中緩存的應用越多,應用間的切換也就越流暢。作為我的一部分工作,我深入了解了Android應用內存泄漏問題,發現大多數都是同一類情況——長時間保持對一個Context的引用(keep a long_lived reference to a Context)。

在 Android里,許多操作都需要使用Context,但絕大部分情況是獲取、加載資源,這也是所有的控件需要在構造函數里接收一個Context參數的原因。一般情況下,我們可以獲取兩種Context,Activity和Application,開發者通常會將第一個也就是Activity作為參數進行傳遞。

</div> </div>

    @Override
protected void onCreate(Bundle state) {
super.onCreate(state);

  TextView label = new TextView(this);  
  label.setText("Leaks are bad");  

  setContentView(label);  
}  </pre><br />

這意味著view持有了整個Activity的引用和他里面的所有東西,比如整個view hierarchy和所有的資源。因此,如果你泄漏了Context(“泄漏”的意思是你保持了他的引用,使得GC不能及時回收),你就泄漏了一大部分內存。如果不夠小心點話泄漏整個activity是很容易的。

當屏幕方向改變時系統會默認destroy當前的activity,保存當前狀態并創建一個新的activity,也就是Android會重新從資源文件中加載應用的視圖。想象一下你的應用里有一幅大圖,但并不像每次旋轉屏幕都重新去加載,將它緩存在內存里最簡單的辦法是將它聲明為static類型:

</div> </div>

    private static 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);  
}  </pre><br />

上面的代碼運行很快,但做法卻是錯誤的,當屏幕旋轉的時候泄漏了整個activity,當一個Drawable關聯到view上,這個view就被設置為 drawable的一個callback,在上面的代碼里,這意味著drawable保持了TextView的引用,而TextView又保持了整個 Activity的引用,也就幾乎關聯了activity里的所有東西。 這個例子是Context泄漏里最簡單的一種情況,你可以在HomeScreen的源碼里看我們是怎么通過把drawable的callback設置為 null來解決這個問題的(搜索unbindDrawables()方法),有趣的是,有時會創建出一條泄漏context的調用鏈,這樣會使你的內存更快的耗盡。
有兩種簡單的方式來避免context相關的內存泄漏,一個是避免context逃出他自己的范圍(avoid escaping the context outside of its own scope),上面的例子展示了靜態引用造成泄漏的情況,但是內部類對外部類的強引用也同樣危險。另一種解決方法是使用Application context,這個context會一直存在,直到你的應用退出,而不會依賴于activity的生命周期。如果你有一個長時間存活的對象,而且對象有 context的引用,記住使用application context,這個你可以很容易地通過Context.getApplicationContext()或者 Activity.getApplication()來獲取。
總之,避免context相關的內存泄漏,記得下面幾條

  • 不要保持長時間對activity context類型的引用(一個對activity的引用應該和這個activity有相同的生命周期)
  • 使用application context而不是activity context
  • 避免在一個activity中使用非靜態(non-static)內部類,如果你不管他的生命周期的話,使用一個靜態內部類,然后在這個類內部維持一個對activity的弱引用,就像在ViewRoot類中的W內部類這樣。
  • 垃圾回收不保證內存泄漏
  • </ul>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!