记一次内存泄露排查

最后在实现一个无限循环的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传给别人,要注意内存泄露,具体看我另外一篇博客

这里还说明另一个问题,就是变量,能不用成员变量就尽可能不要用,否则,一旦内存泄露,会把成员变量的内存也泄露了,我这里就是成员变量致使的内存泄露

相关文章
相关标签/搜索