記一次內存泄露排查

最後在實現一個無限循環的ViewPager,展現圖片,功能實現了,可是運行一段時間以後會掛掉。html

多虧了AndroidStudio的Memory Monitor,發現了內存一直在增加。java

怎麼觸發gc內存都不會減小,肯定了內存泄露了,可是不知哪裏出問題了。數據庫

 

一時想到的排查內存泄露的工具,就是MAT了,可是沒找到AndroidStudio的MAT插件。緩存

只能先把java heap dump出來先,以下圖所示app

dump出來以後,hprof文件會保存在項目下captures目錄,以前一直不知到,找了好久。。。eclipse

若是切換到Captures這個tab,是能夠直接看到HeapSnapshot的,以下圖所示ide

可是這個hprof文件mat不認,須要轉換一下,點擊hprof文件右鍵,轉成標準.hprof文件便可工具

 

 

而後到eclipse用mat插件打開,若是沒有安裝mat插件請自行搜索解決this

window->open perspective->memory analysisurl

在memory analysis界面下

File->Open Heap Dump->選中上面Android Studio轉換以後的.hprof文件便可

在OverView下面點擊Top Consumers, 以下圖。byte佔了大頭,內存基本都給它用了

byte佔用這麼多內存,一想到的就是bitmap沒被釋放了。

回去研究個人代碼

PagerAdapter代碼以下

 1 public class SlidePicPagerAdapter extends PagerAdapter {
 2 
 3     private List<SlidePicModel> mItems;
 4     private int itemSize;
 5 
 6     public void setItems(List<SlidePicModel> items) {
 7         mItems = items;
 8         itemSize = items.size();
 9     }
10 
11     @Override
12     public int getCount() {
13         return ListUtils.isEmpty(mItems) ? 0 : Integer.MAX_VALUE;
14     }
15 
16     @Override
17     public boolean isViewFromObject(View view, Object object) {
18         return view == object;
19     }
20 
21     @Override
22     public Object instantiateItem(ViewGroup container, int position) {
23         Context context = container.getContext();
24         SlidePicModel item = mItems.get(getCurPos(position));
25         ImageView iv = item.getImageView();
26         if(iv == null){
27             iv = new ImageView(context);
28             item.setImageView(iv);
29         }
30         final String imgUrl = ImageUrlExtends.getImageUrl(item.getUrl());
31         Picasso.with(context).load(imgUrl).into(iv);
32         container.addView(iv);
33         return iv;
34     }
35 
36     @Override
37     public void destroyItem(ViewGroup container, int position, Object object) {
38         View imageView = (View) object;
39         container.removeView(imageView);
40     }
41 
42 
43     private int getCurPos(int pos){
44         return pos % mItems.size();
45     }
46 }
View Code

 我用的是Picasso去加載圖片。首先把picasso加載圖片給屏蔽了,發現內存正常了,擦,覺得我發現了個bug,先去給Picasso提Bug去呢。

再看看我代碼,第28行,我把ImageView給緩存到數據源SlidePicModel的成員變量裏了。

SlidePicModel代碼以下

 1 public class SlidePicModel {
 2 
 3     private ImageView imageView;
 4     private String url;
 5 
 6     public SlidePicModel(String url) {
 7         this.url = url;
 8     }
 9 
10     public ImageView getImageView() {
11         return imageView;
12     }
13 
14     public void setImageView(ImageView imageView) {
15         this.imageView = imageView;
16     }
17 
18     public String getUrl() {
19         return url;
20     }
21 
22     public void setUrl(String url) {
23         this.url = url;
24     }
25 }
View Code

 

要想一想,我這個數據源mItems裏面表明好幾百張圖片,而個人應用是無限循環顯示圖片,也便是個人數據庫mItems是不會回收的。

原本顯示完圖片,ImageView是要被回收的,可是,卻被我數據源裏面的model引用着,幾百張圖片的bitmap被引用着,不能回收,app確定內存溢出。

解決方法以下,把ImageView這個成員變量設置成弱引用當內存不足時,ImageView能夠被內存回收。問題解決,代碼以下

 1 public class SlidePicModel {
 2 
 3 
 4     private String url;
 5     private WeakReference<ImageView> mImageViewRef;
 6     public SlidePicModel(String url) {
 7         this.url = url;
 8     }
 9 
10     public ImageView getImageView() {
11         return mImageViewRef == null ? null : mImageViewRef.get();
12     }
13 
14     public void setImageView(ImageView imageView) {
15 //        this.imageView = imageView;
16         mImageViewRef = new WeakReference<ImageView>(imageView);
17     }
18 
19     public String getUrl() {
20         return url;
21     }
22 
23     public void setUrl(String url) {
24         this.url = url;
25     }
26 }
View Code

 內存回收正常。圖片以下

固然了,更好的辦法就是,這個ImageView成員變量徹底沒有存在的必要,可是當時一時把代碼寫錯,出恰好讓我研究了一下內存泄露,對內存泄露有更深的認識。

 

總結:

以前就有看到一些文章在建議,不要把Activity當成靜態成員變量。

其實從我上面出現的問題裏就能夠發現同樣的道理。上面是由於數據源一直存在,沒有被內存回收,因此引用的ImageView也沒有被回收,致使內存溢出。

同理,若是把Activity當成靜態成員變量。那麼它的生命週期就會和app的生命週期同樣,Activity裏所引用的對象都不會被釋放,即便activity已經結束了,這就會致使內存舉出。

使用context的時候,能使用ApplicationContext就使用AplicationContext,若是把Activity當成context傳給別人,要注意內存泄露,具體看我另外一篇博客

這裏還說明另一個問題,就是變量,能不用成員變量就儘可能不要用,否則,一旦內存泄露,會把成員變量的內存也泄露了,我這裏就是成員變量致使的內存泄露

相關文章
相關標籤/搜索