0

0

内存泄漏的原因及解决办法是什么

醉折花枝作酒筹

醉折花枝作酒筹

发布时间:2021-05-17 16:10:33

|

31200人浏览过

|

来源于php中文网

原创

原因及解决方法为:1、使用静态内部类,避免线程造成的内存泄漏;2、使用缓存的convertView构造Adapter,避免使用ListView造成的内存泄漏;3、退出程序前,clear集合里的东西,置为null,避免集合容器中的内存泄露等。

内存泄漏的原因及解决办法是什么

本教程操作环境:windows7系统、Dell G3电脑。

常见的内存泄露造成的原因

1、单例造成的内存泄漏

由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。

示例:防止单例导致内存泄漏的实例

// 使用了单例模式
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

2、非静态内部类创建静态实例造成的内存泄漏

例如,有时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现如下写法:

  public class MainActivity extends AppCompatActivity {

    private static TestResource mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}

3、Handler造成的内存泄漏

示例:创建匿名内部类的静态对象

public class MainActivity extends AppCompatActivity {

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}

1、从Android的角度

当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。另外,主线程的Looper对象会伴随该应用程序的整个生命周期。

2、 Java角度

在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。

对上述的示例进行分析,当MainActivity结束时,未处理的消息持有handler的引用,而handler又持有它所属的外部类也就是MainActivity的引用。这条引用关系会一直保持直到消息得到处理,这样阻止了MainActivity被垃圾回收器回收,从而造成了内存泄漏。

解决方法:将Handler类独立出来或者使用静态内部类,这样便可以避免内存泄漏。

4、线程造成的内存泄漏

示例:AsyncTask和Runnable

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    }

    class MyAsyncTask extends AsyncTask {

        // ...

        public MyAsyncTask(Context context) {
            // ...
        }

        @Override
        protected Void doInBackground(Void... params) {
            // ...
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            // ...
        }
    }

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // ...
        }
    }
}

AsyncTask和Runnable都使用了匿名内部类,那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。

解决方法:将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。

百度智能云·曦灵
百度智能云·曦灵

百度旗下的AI数字人平台

下载

5、资源未关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。

1)比如在Activity中register了一个BraodcastReceiver,但在Activity结束后没有unregister该BraodcastReceiver。

2)资源性对象比如Cursor,Stream、File文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。

3)对于资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉,然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。

4)Bitmap对象不在使用时调用recycle()释放内存。2.3以后的bitmap应该是不需要手动recycle了,内存已经在java层了。

6、使用ListView时造成的内存泄漏

初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的View对象,同时ListView会将这些View对象缓存起来。当向上滚动ListView时,原先位于最上面的Item的View对象会被回收,然后被用来构造新出现在下面的Item。这个构造过程就是由getView()方法完成的,getView()的第二个形参convertView就是被缓存起来的Item的View对象(初始化时缓存中没有View对象则convertView是null)。

构造Adapter时,没有使用缓存的convertView。

解决方法:在构造Adapter时,使用缓存的convertView。

7、集合容器中的内存泄露

我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

解决方法:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

8、WebView造成的泄露

当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。

解决方法:为WebView另外开启一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

更多计算机相关知识,请访问常见问题栏目!

相关专题

更多
Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

9

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
php-src源码分析探索
php-src源码分析探索

共6课时 | 0.5万人学习

微信小程序开发--云开发篇
微信小程序开发--云开发篇

共15课时 | 0.7万人学习

golang和swoole核心底层分析
golang和swoole核心底层分析

共3课时 | 0.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号