美文网首页
Android内存泄漏

Android内存泄漏

作者: 四喜汤圆 | 来源:发表于2018-09-23 21:33 被阅读17次

一、相关概念

1. 什么是内存泄漏

在堆上分配的对象无法被GC回收。对象无用;但对象可达。因为:该对象被一个生命周期更长的对象引用着

2. 内存泄漏的后果:OOM

GC会定时扫描内存,发现不被引用的对象即可回收。如果Activity的引用被某个一直持有,那么返回-进入-退出这个Activity,会导致Activity创建多次,存在多个实例。当用户重复进行上述操作,导致多个Activity内存泄漏时,app运行一段时间堆内存超过系统规定的最大值heapSize,app崩溃。

heapSize:设备分配给app的最大堆内存,例如192M
maxHeapSize:当配置了android:largeHeap="true"时才有的最大堆内存,一般是heapSize的2-3倍,例如512M

ActivityManager manager=(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapSize=manager.getMemoryClass();
int maxHeapSize=manager.getLargeMemoryClass();

3. GC

GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。

将对象的引用关系考虑为图的有向边,有向边从引用者指向被引用者

4. GCRoot

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(一般说的native方法)引用的对象

5. 什么叫内存空间不足

Java4中引用级别(摘自_校招指南)

内存不足:

6. 内存抖动

短时间内大量对象进出新生代(young generation),它伴随着频繁GC

  1. 产生原因
    由大量对象在短时间内被配置而引起,我们要做的就是谨慎对待那些可能创建大量对象的情况。
  2. 如何避免
  • 尽量避免在循环体内创建对象,应把对象创建移到循环体外
  • 自定义view的onDraw()方法会被频繁地调用,所以在这里面不应该频繁地创建对象
  • 当需要大量使用Bitmap时,试着把它们缓存在数组中实现复用
  • 对于能够复用的对象,可以用对象池将它们缓存起来

来自 CoorChice 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_31370269/article/details/52818277?utm_source=copy

二、常见内存泄漏场景

内存泄漏原因:生命周期长的对象引用了生命周期短的对象,导致生命周期短的对象无法被回收

1. 单例??传入Context还是单例吗

public class Singleton {
    // 上下文
    private Context context;
    private static volatile Singleton instance=null;

    private Singleton(Context context){
        this.context=context;
    }

    public static Singleton getInstance(Context context){
        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    instance = new Singleton(context);
                }
            }
        }
        return instance;
    }
}

原因

上述代码是一个单例模式,若将Activity的上下文传入到这个单例类中,会导致Activity无法被回收,内存泄漏。

解决方法

  1. 将单例类中的context用弱引用修饰
public class Singleton {
    // 上下文
    private WeakReference<Activity> context;
    private static volatile Singleton instance=null;

    private Singleton(WeakReference<Activity> context){
        this.context=context;
    }

    public static Singleton getInstance(Activity context){
        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    instance = new Singleton(new WeakReference<Activity>(context));
                }
            }
        }
        return instance;
    }
}
  1. 用Application的上下文代替Activity的上下文
instance = new Singleton(context.getApplicationContext());

2. 非静态内部类造成的内存泄漏

非静态内部类会持有外部类的引用,如果非静态内部类实例的生命周期比它的外部类的生命周期长,那么当销毁外部类时,会导致外部类无法被回收。

public class MainActivity extends AppCompatActivity{
    private static Test test;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        if(test==null){
            test=new Test();
        }
    }

    private class Test{

    }

}

原因

上述代码中test类变量引用了Test实例,其生命周期是整个应用的生命周期,非静态内部类持有外部类MainActivity的引用

解决方法

  1. test变量设置为非static类型,使其生命周期和Activity一样长
  2. Test内部类设置为静态内部类

3. Handler、Runnable作为非静态内部类出现时

 */
public class MainActivity extends AppCompatActivity{

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        },10*1000);
        finish();
    }
}

原因

Handler、Runnable有定时功能,当它们作为非静态内部类时会持有外部类的引用。如果它们的内部有延时操作,在延迟操作还没有发生时,就销毁了外部类,那么外部类对象无法回收,内存泄漏。

解决方法:静态内部类+弱引用

  1. 将Handler、Runnable设置为静态内部类
  2. onDestroy()方法中移除Handler中的消息
public class MainActivity extends AppCompatActivity{
    private Handler mHandler;
    private Runnable mRunnable;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        mHandler=new TestHandler();
        mRunnable=new TestRunnable();
        mHandler.postDelayed(mRunnable,10*1000);
    }

    private static class TestHandler extends Handler{

    }

    private static class TestRunnable implements  Runnable{
        public void run(){

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRunnable);
    }
}

虽然上述代码解决了Handler作为静态内部类时不会持有外部类引用,但是有时我们会自己把Activity的上下文传到Handler里,这种情况下:可通过弱引用的方式引用Context,避免内存泄漏。

public class MainActivity extends AppCompatActivity{
    private Handler mHandler;
    private Runnable mRunnable;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        mHandler=new TestHandler(this);
        mRunnable=new TestRunnable();
        mHandler.postDelayed(mRunnable,10*1000);
        finish();
    }

    private static class TestHandler extends Handler{
        private WeakReference<Context> mContext;

        // 通过构造函数传入上下文
        public TestHandler(Context context) {
            this.mContext = new WeakReference<Context>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Context context=mContext.get();
            if (context != null) {
                // do something
            }

        }
    }

    private static class TestRunnable implements  Runnable{
        public void run(){

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRunnable);
    }
}

参考文献

内存泄漏全解析
Android内存泄漏总结

相关文章

网友评论

      本文标题:Android内存泄漏

      本文链接:https://www.haomeiwen.com/subject/gzwanftx.html