Android内存泄漏个人理解与分析

今天整理一下关于内存泄漏和优化相关,这是个人最近心得,希望能够帮助读者。

下面我们便开始吧。

最近组内在讨论关于内存泄漏与优化的问题,每个人多多少少可能都会遇到这样的问题,总是觉得哪里会出现内存泄漏,而网上对内存泄漏和优化的文章有一大堆,每次看的总是觉得一时能够理解,但是自己却总是用不到或者想不透,最近颇有心得,下面来讲下关于这个问题,结合一些例子,优化和泄漏放在一起。

个人理解有以下几点:
1.首先,关于后台服务,别总是想着要保活,现在Google已经把app后台活动限制的死死的,别想着要反着来,人家这样做肯定是经过仔细考虑才发布的,而且每个版本都还要加强限制,不懂得小伙伴可以大概了解一下Doze模式下面程序活动情况,基本后台活动会不断延时进行,就连用的微信最近放久了也不及时不灵光了。
结论:服务Google推荐使用JobService,一般服务做前台就行,也可以用IntentService(如果与UI无交互)

2.应用返回为杀死,我们经常会在主页面重写返回,让用户点了back之后进入后台页面,这是不友好的。
建议:使用正常退出流程,让应用关闭当前页面,否则这部分页面任然占用内存,导致资源浪费

3.关于Context引用,有Activity的Context,而一般建议使用ApplicationContext,这样使对象引用的是Application,而不是页面的Context,否则存在内存泄漏,这个通俗易懂,但是什么情况是Context引用而容易导致内存泄漏呢,我整理了一下,用一句话来讲:线程或者静态对象直接或者间接引用了ActivityContext。

什么是直接引用?

1.静态对象引用

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     //public static Object ref1;
        RefManager.ref1 = this;
    }
}

这是一个静态对象引用case,这里存在页面关闭activity任然被静态指向导致内存泄漏,我想这样的代码应该每人写。

2.线程引用

public class MyThread extends Thread{
    private Context mContext;

    public MyThread(Context mContext) {
        this.mContext = mContext;
    }
}

Thread mThread = new MyThread(this);
mThread.start();

这种case是线程直接引用Context,这种情况导致页面关闭但是Thread可能还没有执行完成导致Context被引用出现内存泄漏。

以上是2种直接引用this对象(Map引用也差不多),这2种情况都需要使用慎重,直接引用很容易排查,建议使用WeakReference或者在页面推出执行引用置空操作并且释放资源

3.间接引用:

关于间接引用,据我理解,其实就是 非静态内部类/匿名内部类 被静态或者线程引用,为什么这样理解呢?

3.1链式传递引用:顾名思义,就是一个A -> B -> T/S,那么T/S也是引用A的,举例如下:

MyObject obj = new MyObject(MainActivity.this);
Thread mThread = new MyThread(obj);
mThread.start();

这里MyObject对象对Activity引用,而该对象又被MyThread引用,这么按照传递的效果,那么Activity也是间接被MyThread引用了

3.2非静态内部类:

非静态内部类可以访问外部方法(静态内部类则不能访问),也就是说,非静态内部类默认可以完全访问外部方法,这样外部类就是默认被非静态内部类完全引用了,如果将这个非静态内部类传给静态变量或者线程,那么就好比 A -> B -> T/S , 其中A是Context/Activity,B在A里面属于非静态内部类, T/S是线程或静态变量,这样T/S就是间接引用A,在A关闭可以导致内存泄漏,当然了,这里需要注意的是可能B经过很多C或者D或者E,最后到T/S,这就存在一个链式间接引用,这条链上面的每个元素都不会被释放,导致泄漏。下面将结合3.3举例:

3.3匿名内部类:

匿名内部类同3.2非静态内部类,匿名内部类是由new创建并重写或者实现某个方法,假设在A中有个匿名内部类,那么匿名内部类重写或者实现的方法中可以调用A中所有方法,这就存在对A的引用,匿名内部类引用了A,这时候又和上面一样的case了,如果这个匿名内部类经过多次链式传递给了静态变量或者线程,那么页面关闭的时候,匿名内部类不会被释放,那么引用的A也不会被释放,导致内存泄漏,常见的有CallBack,Handler,new构造的所有对象重写或实现的方法会产生引用A内部方法,举个栗子:

public class MainActivity extends Activity{

    private void init(){
        //匿名内部类引用
        MyThread thread = new MyThread(new callBack1() {
            @Override
            public void onResult() {
                processResult();
            }
        });
        //非静态内部类引用
        thread.setCallBack2(new callBack2());
    }

    public interface callBack1{
        void onResult();
    }

    public class callBack2{
        public void invoke(){
            MainActivity.this.processResult();
        }
    }

    public void processResult(){
        //do something
    }
}

此例子是一个Activity里面有一个内部类CallBack2和一个接口,首先是非静态内部类,使用invoke方法调用Activity的onResult,其二是匿名内部类接口callBack1,这里内部类接口callBack1引用了this里面方法导致后台操作不会及时释放Context。

4.资源的打开与关闭

使用IO、File流或者Sqlite、Cursor等资源时要及时关闭,一般都是对应写注册与反注册, Open与Close

5.其他

耗时任务,属性动画间接引用View里面Context,Thread(Runnable),Timer引用This方法导致泄漏。

总结:
关于内存泄漏,目前个人理解就是对页面Context(this)的引用,直接或者间接引用,直接引用容易发现,间接引用需要追踪链接,不管是线程还是静态内部类,引用方法或者view都会造成内存泄漏。如果内部类并没有引用Context(this)里面的方法或者指向则不会造成泄漏

你可能感兴趣的:(Android内存泄漏个人理解与分析)