前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >内存泄漏三问—vivo真题

内存泄漏三问—vivo真题

作者头像
码上积木
发布2020-09-27 14:27:46
5310
发布2020-09-27 14:27:46
举报
文章被收录于专栏:码上积木码上积木

说到性能优化,就不得不提下内存泄漏了,内存泄漏发生的原因以及解决办法你是否都已了解呢?看看今天的三问:

  • 内存泄漏是什么,为什么会发生?
  • 内存泄漏发生的情况有哪些?
  • 该怎么发现和解决内存泄漏?

内存泄漏是什么,为什么会发生?

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

简单点说,手机给我们的应用提供了一定大小的堆内存,在不断创建对象的过程中,也在不断的GC(java的垃圾回收机制),所以内存正常情况下会保持一个平稳的值。但是出现内存泄漏就会导致某个实例,比如Activity的实例,应用被某个地方引用到了,不能正常释放,从而导致内存占用越来越大,这就是内存泄漏

内存泄漏发生的情况有哪些?

主要有四类情况

  • 集合类泄漏
  • 单例/静态变量造成的内存泄漏
  • 匿名内部类/非静态内部类
  • 资源未关闭造成的内存泄漏

1)集合类泄漏

集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。

static List<Object> mList = new ArrayList<>();
   for (int i = 0; i < 100; i++) {
       Object obj = new Object();
      mList.add(obj);
       obj = null;
    }

解决办法就是把集合也释放掉。

  mList.clear();
  mList = null;

2)单例/静态变量造成的内存泄漏

单例模式具有其静态特性,它的生命周期等于应用程序的生命周期,正是因为这一点,往往很容易造成内存泄漏。


public class SingleInstance {

    private static SingleInstance mInstance;
    private Context mContext;

    private SingleInstance(Context context){
        this.mContext = context;
    }

    public static SingleInstance newInstance(Context context){
        if(mInstance == null){
            mInstance = new SingleInstance(context);
        }
        return sInstance;
    }
}


比如这个单例模式,如果我们调用newInstance方法时候把Activity的context传进去,那么就是生命周期长的持有了生命周期短的引用,造成了内存泄漏。要修改的话把context改成context.getApplicationContext()即可。

3)匿名内部类/非静态内部类

非静态内部类他会持有他外部类的强引用,所以就有可能导致非静态内部类的生命周期可能比外部类更长,容易造成内存泄漏,最常见的就是Handler

public class TestActivity extends Activity {
private TextView mText;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);


        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    
        mHandler. sendEmptyMessageDelayed(0, 100000);
    }

怎么修改呢?改成静态内部类,然后弱引用方式修饰外部类

public class TestActivity extends Activity {
    private TextView mText;
    private MyHandler myHandler = new MyHandler(TestActivity.this);
    private MyThread myThread = new MyThread();

    private static class MyHandler extends Handler {

        WeakReference<TestActivity> weakReference;

        MyHandler(TestActivity testActivity) {
            this.weakReference = new WeakReference<TestActivity>(testActivity);

        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            weakReference.get().mText.setText("do someThing");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }

4)资源未关闭造成的内存泄漏

比如:

  • 网络、文件等流忘记关闭
  • 手动注册广播时,退出时忘记unregisterReceiver()
  • Service 执行完后忘记 stopSelf()
  • EventBus 等观察者模式的框架忘记手动解除注册

该怎么发现和解决内存泄漏?

1、使用工具,比如Memory Profiler,可以查看app的内存实时情况,捕获堆转储,就生成了一个内存快照,hprof文件。通过查看文件,可以看到哪些类发生了内存泄漏。

2、使用库,比较出名的就是LeakCanary,导入库,然后运行后,就可以发现app内的内存泄漏情况。

这里说下LeakCanary的原理:

  • 监听 首先通过ActivityLifecycleCallbacksFragmentLifeCycleCallbacks监听Activity和Fragment的生命周期。
  • 判断 然后在销毁的生命周期中判断对象是否被回收。弱引用在定义的时候可以指定引用对象和一个 ReferenceQueue,通过该弱引用是否被加入ReferenceQueue就可以判断该对象是否被回收。
  • 分析 最后通过haha库来分析hprof文件,从而找出类之前的引用关系。

参考文章

https://juejin.im/post/6844903641032163336

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 内存泄漏是什么,为什么会发生?
  • 内存泄漏发生的情况有哪些?
  • 该怎么发现和解决内存泄漏?
  • 参考文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档