请选择 进入手机版 | 继续访问电脑版

[Android] 详解Android内存走漏 及优化方案

[复制链接]
查看61 | 回复8 | 2021-9-14 06:51:29 | 显示全部楼层 |阅读模式
目次

一、常见的内存泄漏 应用场景?

1、单例的不得当 利用

单例是我们开辟 中最常见和利用 最频仍 的计划 模式之一,以是 假如 利用 不当就会导致内存泄漏 。由于 单例的静态特性使得它的生命周期同应用的生命周期一样长,假如 一个对象已经没有用 处了,但是单例还持有它的引用,那么在整个应用程序的生命周期这个对象都不能正常被回收,从而导致内存泄漏 。

如:

  1. public class App {
  2. private static App sInstance;
  3. private Context mContext;
  4. private App(Context context) {
  5. this.mContext = context;
  6. }
  7. public static App getInstance(Context context) {
  8. if (sInstance == null) {
  9. sInstance = new App(context);
  10. }
  11. return sInstance;
  12. }
  13. }
复制代码

调用getInstance(Context context)方法时传入的上下文假如 为当前活动Activity或者当前服务的Service以及当前fragment的上下文,当他们烧毁 时,这个静态单例sIntance还会持用他们的引用,从而导致当前活动、服务、fragment等对象不能被回收开释 ,从而导致内存走漏 。这种上下文的利用 很多时间 处理不当就会导致内存走漏 ,必要 我们多注意 编码规范。

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

静态变量存储在方法区,它的生命周期从类加载开始,到整个历程 竣事 。一旦静态变量初始化后, 它所持有的引用只有等到历程 竣事 才会开释 。

如下代码:

  1. public class MainActivity extends AppCompatActivity {
  2. private static Info sInfo;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. if (sInfo != null) {
  8. sInfo = new Info(this);
  9. }
  10. }
  11. }
  12. class Info {
  13. public Info(Activity activity) {
  14. }
  15. }
复制代码

Info 作为 Activity 的静态成员,并且持有 Activity 的引用,但是 sInfo 作为静态变量,生命周期 肯定比 Activity 长。以是 当 Activity 退出后,sInfo 仍然 引用了 Activity,Activity 不能被回收, 这就导致了内存泄漏 。 在 Android 开辟 中,静态持有很多时间 都有大概 由于 其利用 的生命周期不同等 而导致内存泄漏 , 以是 我们在新建静态持有的变量的时间 必要 多思量 一下各个成员之间的引用关系,并且只管 少地 利用 静态持有的变量,以避免发生内存泄漏 。当然,我们也可以在得当 的时间 讲静态量重置为 null, 使其不再持有引用,如许 也可以避免内存泄漏 。

3、非静态内部类导致内存泄漏

非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期 比外部类对象的生命周期长时,就会导致内存泄漏 。这类内存走漏 很典型的Handler的利用 ,这么一说大家应该就很熟悉 了吧,大家都知道怎么处理这类内存走漏 。

Handler的利用 示例:

  1. private void start() {
  2. Message msg = Message.obtain();
  3. msg.what = 1;
  4. mHandler.sendMessage(msg);
  5. }
  6. private Handler mHandler = new Handler() {
  7. @Override
  8. public void handleMessage(Message msg) {
  9. if (msg.what == 1) {
  10. // ui更新
  11. }
  12. }
  13. };
复制代码

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 的内存泄漏 。

怎样 避免:
1、采用静态内部类+弱引用的方式

  1. private static class MyHandler extends Handler {
  2. private WeakReference<MainActivity> activityWeakReference;
  3. public MyHandler(MainActivity activity) {
  4. activityWeakReference = new WeakReference<>(activity);
  5. }
  6. @Override
  7. public void handleMessage(Message msg) {
  8. MainActivity activity = activityWeakReference.get();
  9. if (activity != null) {
  10. if (msg.what == 1) {
  11. // 做相应逻辑
  12. }
  13. }
  14. }
  15. }
复制代码

mHandler 通过弱引用的方式持有 Activity,当 GC 实验 垃圾回收时,遇到 Activity 就会回收并释 放所占据的内存单元。如许 就不会发生内存泄漏 了。但是 msg 还是有大概 存在消息队列 MessageQueue 中。

2、Activity 烧毁 时就将 mHandler 的回调和发送的消息给移除掉。

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. mHandler.removeCallbacksAndMessages(null);
  5. }
复制代码

非静态内部类造成内存泄漏 还有一种环境 就是利用 Thread 或者 AsyncTask异步调用:
如示例:
Thread :

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. try {
  10. Thread.sleep(2000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }).start();
  16. }
复制代码

AsyncTask:

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. new AsyncTask<Void, Void, Void>() {
  7. @Override
  8. protected Void doInBackground(Void... params) {
  9. // UI线程处理
  10. try {
  11. Thread.sleep(2000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. return null;
  16. }
  17. }.execute();
  18. }
  19. }
复制代码

以上新建的子线程 Thread 和 AsyncTask 都是匿名内部类对象,默认就隐式的持有外部 Activity 的引用, 导致 Activity 内存泄漏 。要避免内存泄漏 的话还是必要 像上面 Handler 一样利用 采用静态内部类+弱引用的方式(如上面Hanlder采用静态内部类+弱引用的方式)。

4、未取消注册或回调导致内存泄漏

比如我们在 Activity 中注册广播,假如 在 Activity 烧毁 后不取消注册,那么这个刚播会不停 存在 体系 中,同上面所说的非静态内部类一样持有 Activity 引用,导致内存泄漏 。因此注册广播后在 Activity 烧毁 后肯定 要取消注册。

  1. this.unregisterReceiver(mReceiver);
复制代码

在注册观察则模式的时间 ,假如 不及时取消也会造成内存泄漏 。比如利用 Retrofit+RxJava 注册网络哀求 的观察者回调,同样作为匿名内部类持有外部引用,以是 必要 记得在不用或者烧毁 的时间 取消注册。

5、定时器Timer 和 TimerTask 导致内存泄漏

当我们 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 掉属性动画,避免发生内存走漏 。

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. mAnimator.cancel();
  5. }
复制代码

9、WebView 造成内存泄漏

关于 WebView 的内存泄漏 ,由于 WebView在加载网页后会长期占用内存而不能被开释 ,因此我 们在 Activity 烧毁 后要调用它的 destory()方法来烧毁 它以开释 内存。
别的 在查阅 WebView 内存泄漏 相干 资料时看到这种环境 : Webview 下面的 Callback 持有 Activity 引用,造成 Webview 内存无法开释 ,即使是调用了 Webview.destory()等方法都无法办理 题目 (Android5.1 之后)

终极 的办理 方案是:在烧毁 WebView 之前必要 先将 WebView 从父容器中移除,然后在烧毁 WebView。

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. // 先从父控件中移除
  5. WebView mWebViewContainer.removeView(mWebView);
  6. mWebView.stopLoading();
  7. mWebView.getSettings().setJavaScriptEnabled(false);
  8. mWebView.clearHistory();
  9. mWebView.removeAllViews();
  10. mWebView.destroy();
  11. }
复制代码

总结

构造单例的时间 只管 别用 Activity 的引用;
静态引用时注意 应用对象的置空或者少用静态引用;
利用 静态内部类+软引用代替非静态内部类;
及时取消广播或者观察者注册;
耗时使命 、属性动画在 Activity 烧毁 时记得 cancel;
文件流、Cursor 等资源及时关闭; Activity 烧毁 时 WebView 的移除和烧毁 。

下一篇继续:详解Android内存优化策略

ps:内存走漏 是开辟 中的一个痛点,必要 我们有很好的精良 编码风俗 。奥里给!!!!!!!!!

到此这篇关于详解Android内存泄漏 及优化方案一的文章就先容 到这了,更多相干 Android内存优化内容请搜索 脚本之家从前 的文章或继续欣赏 下面的相干 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

avatar 天使798 | 2021-9-14 09:08:27 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的源码论坛他的站点都是商业源码,还是免费下载的那种!特别好用。访问地址:http://www.mxswl.com 猫先森网络
回复

使用道具 举报

avatar 阳子1989 | 2021-9-14 13:33:41 | 显示全部楼层
admin楼主会死的很有节奏的!
回复

使用道具 举报

avatar Abby_guguk | 2021-10-9 08:28:08 | 显示全部楼层
禽兽不如应该不是说admin楼主的的吧?
回复

使用道具 举报

avatar 123456811 | 2021-10-11 11:43:31 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的源码论坛他的站点都是商业源码,还是免费下载的那种!特别好用。访问地址:http://www.mxswl.com 猫先森网络
回复

使用道具 举报

avatar 123457468 | 2021-10-11 11:43:34 | 显示全部楼层
今天的心情很不错啊
回复

使用道具 举报

admin楼主是一个典型的文艺青年啊!
回复

使用道具 举报

经典,收藏了!
回复

使用道具 举报

admin楼主的病历本丢我这里了!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则