OOM问题

作者: hhxx2yd | 来源:发表于2018-07-02 19:27 被阅读0次

    OOM是开发中会经常遇见的一类问题,其中很多原因是可以在写代码阶段就可以排查出来的,本文结合之前解决的OOM的问题,列举了常见的代码编写时的注意事项。

    static 引用

    OOM问题的根源其实就是由于生命周期的不同步导致一些内存无法释放,常见的就是拥有长生命周期的引用了短生命周期的,导致其无法释放,这里面最常见的当属static引用:

    DrmManager.getInstance().init(this);
    

    Activity的onCreat方法中进行init操作,看着没什么问题继续往下走,主要关注传入的this最后到了哪里:

    private static MtkDrmManager mMtkDrmManager = null;
    
    public static MtkDrmManager getInstance(Context context) {
        if(mMtkDrmManager == null) {
            mMtkDrmManager = new MtkDrmManager(context);
        }
    
        return mMtkDrmManager;
    }
    

    从上面代码可以看到mMtkDrmManager 这个static变量最终引用了这个this,而this就是我们在上面传入的Activity,如果你这样写代码就会导致传入的Acitivity被泄露。

    如何修改?
    因static的变量是伴随整个进程的,其生命周期很长,因此需要找到匹配其生命周期的,那么一般来说就进程context来替代Acitivity。

    DrmManager.getInstance().init(this.getApplicationContext());
    

    Fragment泄露

    这个容易被忽视,看下如下代码:

    public void initSlomoEngine(LaunchThemeListener launchThemeListener) {
        this.mLaunchThemeListener = launchThemeListener;
        mSlowMoEngine = new SlowMoEngine();
        mSlowMoEngine.init(this);
    }
    

    Activity的init方法,再看调用这个init的地方:

    mContext.initSlomoEngine(this);
    

    上面的方法是在Fragment中调用的,将fragment传入。这样写代码会有什么问题呢?
    Activity中的mLaunchThemeListener会持有Acitivity的应用,当fragment销毁后,因其被持有导致无法释放对应的内存,fragment被泄露,泄露的内存只能等到Acitivity销毁后才能释放。

    如何修改?
    也很简单,就是在fragment生命周期结束的时候,将mLaunchThemeListener的引用置null,具体代码修改如下:

    //Fragment onDestroy方法
     public void onDestroy() {
            mContext.destorySlomoEngine();
    }
    
    //Acitivty 置null操作
    public void destorySlomoEngine() {
        mLaunchThemeListener = null;
        mSlowMoEngine = null;
    }
    

    线程泄露

    应用的线程一定要留意线程的创建和结束时机,如果出现不匹配的情况则很容易引起线程泄露,参考如下代码:

    //Acitivity的onResume()调用如下方法开启线程
    public void resume() {
        if (mSource != null) {
            mSource.addContentListener(mSourceListener);
        }
        mReloadTask = new ReloadTask();
        mReloadTask.start();
    }
    //Acitivity的onStop()调用如下方法结束线程
    public void pause() {
        if (mReloadTask != null) {
            mReloadTask.terminate();
            mReloadTask = null;
        }
    }
    

    上述线程的创建可结束并不匹配,在一些场景中会出现问题,如当页面有弹出框出现时, Activity什么周期会走onPause,然后当弹出框消失会走onResume方法。如果不断重复上面的场景,则会发现线程在不断的被创建,但是并没有被合理的结束,因为在这种场景下Activity的onStop方法并没有被调用。

    如何修改?
    这个问题是在项目中真实遇到的问题,修改的方法就是把线程创建放在onStart方法中,这样就可以和onStop进行对应,从而避免问题。

    如何排查OOM问题?

    除了写代码的时候注意一些坑之外,如何主动的排查代码中存在的一些内存泄露隐患呢?Android lint也许会有一些帮助,可以在Android studio中找到:

    Analyze--> Inspect Code

    这个操作会对全部代码进行扫描,给出优化意见,耗时较长。具体优化项可以看下面的截图


    Lint.png

    如果只是想针对内存泄露做检查,可在参考上面的图中只勾选Android > Lint > Performance进行排查,或者直接通过具体的name来排查单条case,输入关键字leak过滤即可。

    Analyze--> Run Inspection by name


    Leak.png

    总结

    OOM问题其实是需要开发者重视的,因为每一个小的泄露都是在侵占这用户的内存资源,多个APP累加起来就会影响越来越大。

    相关文章

      网友评论

          本文标题:OOM问题

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