因爲咱們的線程是Activity的內部類,因此MyThread中保存了Activity的一個引用,當MyThread的run函數沒有結束 時,MyThread是不會被銷燬的,所以它所引用的老的Activity也不會被銷燬,所以就出現了內存泄露的問題。 java
這種線程致使的內存泄露問題應該如何解決呢?
第1、將線程的內部類,改成靜態內部類。
第2、在線程內部採用弱引用保存Context引用。
另外,咱們都知道Hanlder是線程與Activity通訊的橋樑,咱們在開發好多應用中會用到線程,有些人處理不當,會致使當程序結束時,線程並無 被銷燬,而是一直在後臺運行着,當咱們從新啓動應用時,又會從新啓動一個線程,周而復始,你啓動應用次數越多,開啓的線程數就越多,你的機器就會變得越 慢。
package com.tutor.thread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class ThreadDemo extends Activity {
private static final String TAG = "ThreadDemo";
private int count = 0;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
//爲了方便 查看,咱們用Log打印出來
Log.e(TAG, Thread.currentThread().getName() + " " +count);
count++;
setTitle("" +count);
//每2秒執行一次
mHandler.postDelayed(mRunnable, 2000);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//經過Handler啓動線程
mHandler.post(mRunnable);
}
}
因此咱們在應用退出時,要將線程銷燬,咱們只要在Activity中的,onDestory()方法處理一下就OK了,以下代碼所示:
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
3.超級大胖子Bitmap
能夠說出現OutOfMemory問題的絕大多數人,都是由於Bitmap的問題。由於Bitmap佔用的內存實在是太多了,它是一個「超級大胖子」,特別是分辨率大的圖片,若是要顯示多張那問題就更顯著了。
如何解決Bitmap帶給咱們的內存問題?
第1、及時的銷燬。
雖然,系統可以確認Bitmap分配的內存最終會被銷燬,可是因爲它佔用的內存過多,因此極可能會超過java堆的限制。所以,在用完Bitmap時,要 及時的recycle掉。recycle並不能肯定當即就會將Bitmap釋放掉,可是會給虛擬機一個暗示:「該圖片能夠釋放了」。
第2、設置必定的採樣率。
有時候,咱們要顯示的區域很小,沒有必要將整個圖片都加載出來,而只須要記載一個縮小過的圖片,這時候能夠設置必定的採樣率,那麼就能夠大大減少佔用的內存。以下面的代碼:
private ImageView preview;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//圖片寬高都爲原來的二分之一,即圖片爲原來的四分之一
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options); preview.setImageBitmap(bitmap);
第3、巧妙的運用軟引用(SoftRefrence)
有些時候,咱們使用Bitmap後沒有保留對它的引用,所以就沒法調用Recycle函數。這時候巧妙的運用軟引用,可使Bitmap在內存快不足時獲得有效的釋放。
4.行蹤詭異的Cursor
Cursor是Android查詢數據後獲得的一個管理數據集合的類,正常狀況下,若是查詢獲得的數據量較小時不會有內存問題,並且虛擬機可以保證Cusor最終會被釋放掉。
然而若是Cursor的數據量特表大,特別是若是裏面有Blob信息時,應該保證Cursor佔用的內存被及時的釋放掉,而不是等待GC來處理。而且Android明顯是傾向於編程者手動的將Cursor close掉
5.構造Adapter時,沒有使用緩存的 convertView
描述:
以構造ListView的BaseAdapter爲例,在BaseAdapter中提升了方法:
public View getView(int position, View convertView, 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 --> void addScrapView(View scrap) 方法。
示例代碼:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
... ...
return view;
}
修正示例代碼:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
populate(view, getItem(position));
...
} else {
view = new Xxx(...);
...
}
return view;
}
小結:
static:引用了大對象如context
線程:切屏時Activity由於線程引用而沒有如期被銷燬;handler有關,Activity意外終止但線程還在
Bitmap:要及時recycle,下降採樣率
Cursor:要及時關閉
Adapter:沒有使用緩存的convertView
4、內存泄漏調試:
(1).內存監測工具 DDMS --> Heap
不管怎麼當心,想徹底避免bad code是不可能的,此時就須要一些工具來幫助咱們檢查代碼中是否存在會形成內存泄漏的地方。Android tools中的DDMS就帶有一個很不錯的內存監測工具Heap(這裏我使用eclipse的ADT插件,並以真機爲例,在模擬器中的狀況相似)。用 Heap監測應用進程使用內存狀況的步驟以下:
1. 啓動eclipse後,切換到DDMS透視圖,並確認Devices視圖、Heap視圖都是打開的;
2. 將手機經過USB連接至電腦,連接時須要確認手機是處於「USB調試」模式,而不是做爲「Mass Storage」;
3. 連接成功後,在DDMS的Devices視圖中將會顯示手機設備的序列號,以及設備中正在運行的部分進程信息;
4. 點擊選中想要監測的進程,好比system_process進程;
5. 點擊選中Devices視圖界面中最上方一排圖標中的「Update Heap」圖標;
6. 點擊Heap視圖中的「Cause GC」按鈕;
7. 此時在Heap視圖中就會看到當前選中的進程的內存使用量的詳細狀況。
說明:
a) 點擊「Cause GC」按鈕至關於向虛擬機請求了一次gc操做;
b) 當內存使用信息第一次顯示之後,無須再不斷的點擊「Cause GC」,Heap視圖界面會定時刷新,在對應用的不斷的操做過程當中就能夠看到內存使用的變化;
c) 內存使用信息的各項參數根據名稱便可知道其意思,在此再也不贅述。
如何才能知道咱們的程序是否有內存泄漏的可能性呢。這裏須要注意一個值:Heap視圖中部有一個Type叫作data object,即數據對象,也就是咱們的程序中大量存在的類類型的對象。在data object一行中有一列是「Total Size」,其值就是當前進程中全部Java數據對象的內存總量,通常狀況下,這個值的大小決定了是否會有內存泄漏。能夠這樣判斷:
a) 不斷的操做當前應用,同時注意觀察data object的Total Size值;
b) 正常狀況下Total Size值都會穩定在一個有限的範圍內,也就是說因爲程序中的的代碼良好,沒有形成對象不被垃圾回收的狀況,因此說雖然咱們不斷的操做會不斷的生成不少對 象,而在虛擬機不斷的進行GC的過程當中,這些對象都被回收了,內存佔用量會會落到一個穩定的水平;
c) 反之若是代碼中存在沒有釋放對象引用的狀況,則data object的Total Size值在每次GC後不會有明顯的回落,隨着操做次數的增多Total Size的值會愈來愈大,
直到到達一個上限後致使進程被kill掉。
d) 此處已system_process進程爲例,在個人測試環境中system_process進程所佔用的內存的data object的Total Size正常狀況下會穩定在2.2~2.8之間,而當其值超過3.55後進程就會被kill。
總之,使用DDMS的Heap視圖工具能夠很方便的確認咱們的程序是否存在內存泄漏的可能性。
(2).內存分析工具 MAT(Memory Analyzer Tool)
(一) 生成.hprof文件
(二) 使用MAT導入.hprof文件
(三) 使用MAT的視圖工具分析內存 android