zoukankan      html  css  js  c++  java
  • 记一次内存泄露排查

    最后在实现一个无限循环的ViewPager,展示图片,功能实现了,但是运行一段时间之后会挂掉。

    多亏了AndroidStudio的Memory Monitor,发现了内存一直在增长。

    怎么触发gc内存都不会减少,确定了内存泄露了,但是不知哪里出问题了。

    一时想到的排查内存泄露的工具,就是MAT了,但是没找到AndroidStudio的MAT插件。

    只能先把java heap dump出来先,如下图所示

    dump出来之后,hprof文件会保存在项目下captures目录,之前一直不知到,找了很久。。。

    如果切换到Captures这个tab,是可以直接看到HeapSnapshot的,如下图所示

    但是这个hprof文件mat不认,需要转换一下,点击hprof文件右键,转成标准.hprof文件即可

     

    然后到eclipse用mat插件打开,如果没有安装mat插件请自行搜索解决

    window->open perspective->memory analysis

    在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传给别人,要注意内存泄露,具体看我另一篇博客

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

  • 相关阅读:
    穷举和迭代
    for循环练习题
    case when then else end 用法
    如何将数据库账号(用户)解锁
    比赛安排
    How to spend you day ?
    异常-问题型
    重载和重写的区别
    new关键字的理解-问题型
    源辰项目-1
  • 原文地址:https://www.cnblogs.com/baron89/p/4654307.html
Copyright © 2011-2022 走看看