美文网首页
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