iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >详解Android内存泄露及优化方案
  • 886
分享到

详解Android内存泄露及优化方案

2024-04-02 19:04:59 886人浏览 八月长安
摘要

目录一、常见的内存泄露应用场景?1、单例的不恰当使用 2、静态变量导致内存泄露 3、非静态内部类导致内存泄露 4、未取消注册或回调导致内存泄露 5、定时器Timer 和 TimerT

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

1、单例的不恰当使用

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

如:


public class App {
    private static App sInstance;

    private Context mContext;

    private App(Context context) {
        this.mContext = context;
    }

    public static App getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new App(context);
        }
        return sInstance;
    }
}

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

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

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

如下代码:


public class MainActivity extends AppCompatActivity {
    private static Info sInfo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (sInfo != null) {
            sInfo = new Info(this);
        }
    }
}

class Info {
    public Info(Activity activity) {
    }
}

Info 作为 Activity 的静态成员,并且持有 Activity 的引用,但是 sInfo 作为静态变量,生命周期 肯定比 Activity 长。所以当 Activity 退出后,sInfo 仍然引用了 Activity,Activity 不能被回收, 这就导致了内存泄露。 在 Android 开发中,静态持有很多时候都有可能因为其使用的生命周期不一致而导致内存泄露, 所以我们在新建静态持有的变量的时候需要多考虑一下各个成员之间的引用关系,并且尽量少地 使用静态持有的变量,以避免发生内存泄露。当然,我们也可以在适当的时候讲静态量重置为 null, 使其不再持有引用,这样也可以避免内存泄露。

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

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

Handler的使用示例:


    private void start() {
        Message msg = Message.obtain();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                // ui更新
            }
        }
    };

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、采用静态内部类+弱引用的方式


 private static class MyHandler extends Handler {
        private WeakReference<MainActivity> activityWeakReference;

        public MyHandler(MainActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = activityWeakReference.get();
            if (activity != null) {
                if (msg.what == 1) {
                    // 做相应逻辑 
                }
            }
        }
    }

mHandler 通过弱引用的方式持有 Activity,当 GC 执行垃圾回收时,遇到 Activity 就会回收并释 放所占据的内存单元。这样就不会发生内存泄露了。但是 msg 还是有可能存在消息队列 MessageQueue 中。

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


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

非静态内部类造成内存泄露还有一种情况就是使用 Thread 或者 AsyncTask异步调用:
如示例:
Thread :


public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

AsyncTask:


public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) { 
                // UI线程处理
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }.execute();
    }
}

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

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

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


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


  @Override
    protected void onDestroy() {
        super.onDestroy();
        mAnimator.cancel();
    }

9、WebView 造成内存泄露

关于 WebView 的内存泄露,因为 WebView在加载网页后会长期占用内存而不能被释放,因此我 们在 Activity 销毁后要调用它的 destory()方法来销毁它以释放内存。
另外在查阅 WebView 内存泄露相关资料时看到这种情况: Webview 下面的 Callback 持有 Activity 引用,造成 Webview 内存无法释放,即使是调用了 Webview.destory()等方法都无法解决问题(Android5.1 之后)

最终的解决方案是:在销毁 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();
    }

总结

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

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

ps:内存泄漏是开发中的一个痛点,需要我们有很好的良好编码习惯。奥里给!!!!!!!!!

到此这篇关于详解Android内存泄露及优化方案一的文章就介绍到这了,更多相关Android内存优化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解Android内存泄露及优化方案

本文链接: https://www.lsjlt.com/news/135313.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解Android内存泄露及优化方案
    目录一、常见的内存泄露应用场景?1、单例的不恰当使用 2、静态变量导致内存泄露 3、非静态内部类导致内存泄露 4、未取消注册或回调导致内存泄露 5、定时器Timer 和 TimerT...
    99+
    2024-04-02
  • vue:内存泄露详解
    什么是内存泄露?内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。new了一个对象之后 ,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作用域导致被销毁,...
    99+
    2024-04-02
  • 分析Android常见的内存泄露和解决方案
    目录一、前言二、Android 内存泄露场景2.1、非静态内部类的静态实例2.2、多线程相关的匿名内部类/非静态内部类2.3、Handler 内存泄露2.4、静态 Activity ...
    99+
    2024-04-02
  • Android内存泄露怎么解决
    解决Android内存泄露问题的方法有以下几种:1. 避免长生命周期的引用:确保在不使用时及时释放对象的引用,如Activity中的...
    99+
    2023-09-29
    android
  • Java 内存泄露问题详解
    目录 1、什么是内存泄露? 2、Java 中可能导致内存泄露的场景 3、长生命周期对象持有短生命周期对象引用造成的内存泄露问题示例 4、静态集合类持有对象引用造成内存泄露问题的示例 1、什么是内存泄露?         内存泄露指的是程...
    99+
    2023-09-08
    Java 内存泄露
  • android内存泄露怎么查看和解决
    Android内存泄露是指内存中的对象无法被及时释放,导致内存占用过高,影响应用的性能和稳定性。以下是查看和解决Android内存泄...
    99+
    2024-03-13
    android
  • 详解Android中Bitmap及其内存优化
    小编这次要给大家分享的是详解Android中Bitmap及其内存优化,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。Android Bitmap详解及Bitmap的内存优化一、Bitmap:Bitmap...
    99+
    2023-05-31
    android bitmap roi
  • C语言内存泄露很严重的解决方案
    目录1.前言2.内存泄漏问题原理2.1堆内存在C代码中的存储方式2.2堆内存的获取方法2.3内存泄漏三要素2.4内存释放误区3.内存泄漏问题检视方法1.前言 最近部门不同产品接连出现...
    99+
    2024-04-02
  • Flex内存优化原则和内存泄露的示例分析
    这篇文章将为大家详细讲解有关Flex内存优化原则和内存泄露的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Flex性能优化常用手法众所周知,目前国内的宽带应用并不是像很多发达国家发达,个人应用带宽...
    99+
    2023-06-17
  • Java语言中的内存泄露代码详解
    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存。理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同。JAVA中的内存管理...
    99+
    2023-05-30
    java 内存泄露 实例
  • 详解LeakCanary分析内存泄露如何实现
    目录前言LeakCanary的使用LeakCanary原理源码浅析初始化使用总结前言 平时我们都有用到LeakCanary来分析内存泄露的情况,这里可以来看看LeakCanary是如...
    99+
    2022-12-08
    LeakCanary分析内存泄露 LeakCanary 内存泄露
  • 详解Android内存优化策略
    目录前言一、内存优化策略二、具体优化的点1.避免内存泄漏2.Bitmap等大对象的优化策略(1) 优化Bitmap分辨率 (2) 优化单个像素点内存 (3) Bitmap的缓存策略 ...
    99+
    2024-04-02
  • 怎么解析Flex内存泄露常见现象及解决方法
    怎么解析Flex内存泄露常见现象及解决方法,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Flex性能优化常用手法总结众所周知,目前国内的宽带应用并不是像很多发达...
    99+
    2023-06-17
  • 详解windows下C/C++的内存泄露检测
    目录检测内存泄露定位内存泄露总结检测内存泄露 利用Visual Studio调试器和CRT库提供的检测(malloc和new均适用) 程序只从单一位置退出时: 1.需要以下头文件: ...
    99+
    2024-04-02
  • 内存泄露导致Android中setVisibility()失效怎么解决
    本篇内容介绍了“内存泄露导致Android中setVisibility()失效怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、前情...
    99+
    2023-07-02
  • Python实现内存泄露排查的示例详解
    一般情况下只有需要长期运行的项目才会去关注内存的增长情况,即使是很小部分的内存泄露经过长期的运行仍然会产生很大的隐患。 python本身也是支持垃圾的自动回收的,但是在特定的情况下也...
    99+
    2023-01-28
    Python内存泄露排查 Python内存泄露 Python内存
  • Android 内存泄漏的常见原因及其对应的解决方案
    Android 内存泄漏 Android应用程序中常见的内存泄漏原因有很多,以下是一些常见的原因及对应的解决方案: 1. 静态引用导致的内存泄漏: 静态变量持有对Activity或Fragment的引...
    99+
    2023-09-15
    android
  • Android中使用Handler造成的内存泄露如何解决
    这篇文章将为大家详细讲解有关Android中使用Handler造成的内存泄露如何解决,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、什么是内存泄露?  Java使用有向图机制,通过GC自动...
    99+
    2023-05-30
    android handler
  • Android中使用webview时出现内存泄露如何解决
    Android中使用webview时出现内存泄露如何解决,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1.避免在xml直接写webview控件,这样会引用activity,...
    99+
    2023-05-31
    android webview
  • C语言动态内存泄露常见问题内存分配改进方法详解
    目录一、例题二、2种改进方法法1:二级指针(传址调用)法2:返回指针总结一、例题 试问该段代码能打印什么,或者不能打印什么,说出理由 #define _CRT_SECURE_NO...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作