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

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

  • 相关阅读:
    Qt计算器开发(三):执行效果及项目总结
    [HNOI2019]校园旅行
    How to fix nuget Unrecognized license type MIT when pack
    How to fix nuget Unrecognized license type MIT when pack
    git 通过 SublimeMerge 处理冲突
    git 通过 SublimeMerge 处理冲突
    git 上传当前分支
    git 上传当前分支
    gif 格式
    gif 格式
  • 原文地址:https://www.cnblogs.com/baron89/p/4654307.html
Copyright © 2011-2022 走看看