至少在T-Mobile G1上Android應用在堆上分配的內存大小被限制16MB之內。對於手機來講,這是個不小的內存,可是這仍然遠遠不能知足一些開發者的需求。可是,即便你不打算使用全部的內存空間,你也應該儘量地少用內存,從而使得其餘應用可以運行而不是被殺掉。由於Android可以在內存中保持的應用越多,那麼用戶切換應用的速度就會越快。做爲我工做的一部分,我在作android應用開發的時候也會陷入內存泄漏的問題中,大多數時候內存的泄漏都是因爲犯了相同的錯誤:長期持有了一個Context的引用。
Android上 ,Context能夠用於不少操做,可是大部分時候是用來加載以及使用資源。這就是爲何全部的widgets在他們的構造函數中接受一個Context參數。在通常的android應用中,你一般有兩種Context:分別是Activity和Application。一般的,當咱們的類和方法須要使用到context時,咱們傳遞的是Activity這個context:
[java] view plain
copy
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
setContentView(label);
}
這意味着views擁有一個對整個activity的引用,也就是引用了你的activity所擁有的一切;一般的,這指的是完整的視圖層級結構以及全部它的資源。所以,若是你泄露了一個Context(「 泄漏 」意味着你保持着它的一個引用,從而使它不能被垃圾回收機制回收),就意味着你泄漏了不少的內存。若是你不當心, 泄漏一 個activity的全部資源真的很是容易。
當 屏幕的方向發生改變的時候,系統默認將會銷燬當前的activity而且建立一個新的activity同時保持着原有的狀態。在作這個的時候,Android會從資源從新加載應用的UI。如今,想象一下你寫了一個應用,這個應用有一張很大的bitmap。你不想再每一次旋轉的時候從新加載它。最簡單的方法讓bitmap持續做用而不隨每個方向而從新加載 ,就是把它放進一個靜態域:
[java] view plain
copy
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);
}
這段代碼很快,可是錯誤也很嚴重:它泄漏了第一個activity,這個在第一次屏幕改變時被建立的activity。當一個Drawable被關聯到一個view上,這個view就至關於在drawable上設置的一個回調。在上面的代碼片斷中,這表示drawable有一個TextView的引用,而這個TextView又擁有一個activity的引用(Context),activity依次引用了幾乎全部的東西(取決於你的代碼)。
這個例子展現了一個最簡單的Context 泄漏的狀況,你能夠在Home screen 的源碼中看到咱們是如何解決這個問題的( 查找unbindDrawables() 方法) ,這就是當activity 被銷燬的時候將drawables 的回調設爲null 。有趣的是,你可能創造出一系列context泄漏的狀況有不少,這很是糟糕。他們會是你很快內存溢出。
有兩種簡單的方法來避免context 相關的內存泄漏。最顯著地一個是避免context 逃出他本身的範圍以外。上面的例子就展現了使用靜態引用的狀況,而內部類和他們對外部類的的隱式引用也是一樣危險的。第二種方法是使用Application context 。這個context 的生存週期和你的應用的生存週期同樣長,而不是取決於activity 的生存週期。若是你想保持一個長期生存的對象,而且這個對象須要一個context ,記得使用application 對象。你能夠經過調用Context.getApplicationContext() or Activity.getApplication() 來得到。
總而言之,想要避免context 相關的內存泄漏 ,記住如下幾點:
· 不要對activity 的context 長期引用( 一個activity 的引用的生存週期應該和activity 的生命週期相同)
· 試着使用關於application的 context 來替代和activity相關的context
· 若是一個acitivity 的非靜態內部類的生命週期不受控制,那麼避免使用它;使用一個靜態的內部類而且對其中的activity 使用一個弱引用。解決這個問題的方法是使用一個靜態的內部類,而且對它的外部類有一WeakReference,就像在ViewRoot中內部類W所作的就是這麼個例子。
· 垃圾回收器不能處理內存泄漏的保障。java
2.集合中對象沒清理形成的內存泄漏
咱們一般把一些對象的引用加入到了集合中,當咱們不須要該對象時,並無把它的引用從集合中清理掉,這樣這個集合就會愈來愈大。若是這個集合是static的話,那狀況就更嚴重了。android
3,數據庫,廣播關閉。數據庫
4,構造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對象的話,即浪費資源也浪費時間,也會使得內存佔用愈來愈大。ListView回收list item的view對象的過程能夠查看:
android.widget.AbsListView.java --> voidaddScrapView(View scrap) 方法。
5.Bitmap對象不在使用時調用recycle()釋放內存緩存