美文网首页
Android遇到内存溢出应该怎么解决

Android遇到内存溢出应该怎么解决

作者: 像程序那样去思考 | 来源:发表于2023-02-12 19:09 被阅读0次

从Java的内存分配机制,关于堆栈的理解,到内存泄漏,就是该释放的对象没有被释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收,长时间下去,程序的内存大于系统分配的内存,就会导致内存的溢出(OOM:Out Of Memory),因此,就需要优化。

**内存泄漏的本质**

申请了的内存在不再使用时无法回收。

**Android应用程序内存泄漏的含义**

Android系统为每个应用程序都分配了相应限额的内存。当应用程序中产生的内存泄漏较多时,将会导致应用程序运行所需要的内存超过系统为其分配的限额,这时应用程序就会Crash(崩溃)。

**常见引发内存泄漏的情况**

1.单例引用Activity的Context导致内存泄露

2.静态变量导致内存泄漏

3.非静态内部类导致内存泄露

4.未取消注册或回调导致内存泄露

5.Timer和TimerTask导致内存泄露

6.集合中的对象未清理造成内存泄露

7.属性动画造成内存泄露

8.WebView造成内存泄露

**引发内存泄漏的具体情况及解决方案**

1.单例引用Activity的Context导致内存泄露

单例模式在Android开发中会经常用到,但是如果使用不当就会导致内存泄露。因为单例的静态特性使得它的生命周期同应用的生命周期一样长,从而导致内存泄露。

,,,

public class AppSettings {

private static volatile AppSettings singleton;

private Context mContext;

private AppSettings(Context context) {

    this.mContext = context;

}

public static AppSettings getInstance(Context context) {

    if (singleton == null) {

        synchronized (AppSettings.class) {

            if (singleton == null) {

                singleton = new AppSettings(context);

            }

        }

    }

    return singleton;

}

}

上面代码传入的context参数是Activity、Service等上下文,就会导致内存泄露,为了避免这样单例导致内存泄露,我们可以将context参数改为全局的上下文getApplicationContext():

private AppSettings(Context context) { this.mContext = context.getApplicationContext(); } 全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。单例模式对应应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity的上下文,而是使用Application的上下文。

2.静态变量导致内存泄漏

public class MainActivity2 extends AppCompatActivity {

public static Info sInfo;

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    sInfo = new Info(this);

}

class Info {

    private Context mContext;

    public Info(Context context) {

        this.mContext = context;

    }

}

}

Info作为Activity的静态成员,并且持有Activity的引用,但是sInfo作为静态变量,生命周期肯定比Activity长。所以当Activity退出后,sInfo仍然引用了Activity,Activity不能被回收,这就导致了内存泄露。

3.非静态内部类导致内存泄露

非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

非静态内部类导致的内存泄露在Android开发中有一种典型的场景就是使用Handler

public class MainActivity2 extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    start();

}

private void start() {

    Message message = Message.obtain();

    message.what = 1;

    mHandler.sendMessage(message);

}

private Handler mHandler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

        super.handleMessage(msg);

        if (msg.what == 1) {

            //doNothing

        }

    }

};

}

熟悉Handler消息机制的都知道,mHandler会作为成员变量保存在发送的消息msg中,即msg持有mHandler的引用,而mHandler是Activity的非静态内部类实例,即mHandler持有Activity的引用,那么我们就可以理解为msg间接持有Activity的引用。msg被发送后先放到消息队列MessageQueue中,然后等待Looper的轮询处理(MessageQueue和Looper都是与线程相关联的,MessageQueue是Looper引用的成员变量,而Looper是保存在ThreadLocal中的)。那么当Activity退出后,msg可能仍然存在于消息对列MessageQueue中未处理或者正在处理,那么这样就会导致Activity无法被回收,以致发生Activity的内存泄露。

通常在Android开发中如果要使用内部类,但又要规避内存泄露,一般都会采用静态内部类+弱引用的方式。

MyHandler mHandler;

public static class MyHandler extends Handler {

    private WeakReference<Activity> mActivityWeakReference;

    public MyHandler(Activity activity) {

        mActivityWeakReference = new WeakReference<>(activity);

    }

    @Override

    public void handleMessage(Message msg) {

        super.handleMessage(msg);

    }

} mHandler通过弱引用的方式持有Activity,当GC执行垃圾回收时,遇到Activity就会回收并释放所占据的内存单元。这样就不会发生内存泄露了。上面的做法确实避免了Activity导致的内存泄露,发送的msg不再已经没有持有Activity的引用了,但是msg还是有可能存在消息队列MessageQueue中,所以更好的是在Activity销毁时就将mHandler的回调和发送的消息给移除掉。

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

非静态内部类造成内存泄露还有一种情况就是使用Thread或者AsyncTask。要避免内存泄露的话还是需要像上面Handler一样使用静态内部类+弱应用的方式

4.未取消注册或回调导致内存泄露

比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。在注册观察则模式的时候,如果不及时取消也会造成内存泄露。比如使用Retrofit+RxJava注册网络请求的观察者回调,同样作为匿名内部类持有外部引用,所以需要记得在不用或者销毁的时候取消注册。

5.Timer和TimerTask导致内存泄露

Timer和TimerTask在Android中通常会被用来做一些计时或循环任务,比如实现无限轮播的ViewPager: 当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即cancel掉Timer和TimerTask,以避免发生内存泄漏。

6.集合中的对象未清理造成内存泄露

这个比较好理解,如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏。

7.资源未关闭或释放导致内存泄露

在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。

8.属性动画造成内存泄露

动画同样是一个耗时任务,比如在Activity中启动了属性动画(ObjectAnimator),但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。因此同样要在Activity销毁的时候cancel掉属性动画,避免发生内存泄漏。

9.WebView造成内存泄露

关于WebView的内存泄露,因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存。

最终的解决方案是:在销毁WebView之前需要先将WebView从父容器中移除,然后在销毁WebView。

@Override

protected void onDestroy() {

super.onDestroy();

// 先从父控件中移除WebView

mWebViewContainer.removeView(mWebView);

mWebView.stopLoading();

mWebView.getSettings().setJavaScriptEnabled(false);

mWebView.clearHistory();

mWebView.removeAllViews();

mWebView.destroy();

}

内存泄露在Android内存优化是一个比较重要的一个方面,很多时候程序中发生了内存泄露我们不一定就能注意到,所有在编码的过程要养成良好的习惯。

好了,到此就内存泄露以及Android中常见的内存泄露情况和解决方法到这里差不多。当然,以上观点仅仅是小编的管中窥豹,肯定还有许多未知的场景没有覆盖。如果有疑问或不对的地方,请不吝赐教。如果觉得有收获,请点赞,喜欢哦

相关文章

  • 21年第41周:什么是内存泄露?内存溢出?

    内存泄漏遇到过没?什么原因导致的?怎么解决? 那么内存溢出呢,是什么情况?本文带你理解。 内存溢出 out of ...

  • Caused by: java.lang.OutOfMemory

    最笨拙的方法:解决Android[http://lib.csdn.net/base/android] 内存溢出 其...

  • Bitmap加载、缓存机制

    在Android开发中经常会遇到关于图片加载、缓存的问题,经常会遇到内存溢出OOM,Android对单个应用内存加...

  • Android 优化

    Android 内存优化内存溢出 , 内存泄漏 , 内存泄漏过多就导致内存溢出 Android 系统垃圾回收机制内...

  • Android内存泄漏原因及解决的总结

    分三步说明Android内存泄漏的原因及解决,“内存泄漏与内存溢出的区别”,“引用方式”,“常见引发原因与解决方案...

  • Android 内存优化

    Android内存泄露容易导致内存溢出,又称为OOM。 内存泄漏怎么产生的 资源对象没关闭造成的内存泄漏 构造Ad...

  • Android OOM 解决方案

    Android OOM 解决方案 - 银色的流星 - 博客园 Out of Memory(内存溢出) 几乎是每个A...

  • PHP学习笔记-内存溢出解决方案

    一.内存溢出解决方案 在做数据统计分析时,经常会遇到大数组,可能会发生内存溢出,这里分享一下我的解决方案。 假定日...

  • JVM学习系列学习七

    5:实战:内存溢出的定位与分析 内存溢出在日常工作中,这个错误很容易遇到。遇到内存溢出,首先我们需要快速定位内存溢...

  • Android内存溢出的解决思路

    解决方法:https://juejin.im/entry/589542ed2f301e0069054007

网友评论

      本文标题:Android遇到内存溢出应该怎么解决

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