阅读更多

0顶
0踩

移动开发
本文来自:http://blog.nimbledroid.com

像Java这样具有垃圾回收功能的语言的好处之一,就是程序员无需手动管理内存分配。这减少了段错误(segmentation fault)导致的闪退,也减少了内存泄漏导致的堆空间膨胀,让编写的代码更加安全。然而,Java 中依然有可能发生内存泄漏。所以你的安卓 APP 依然有可能浪费了大量的内存,甚至由于内存耗尽(OOM)导致闪退。

传统的内存泄漏是由忘记释放分配的内存导致的,而逻辑上的内存泄漏则是由于忘记在对象不再被使用的时候释放对其的引用导致的。如果一个对象仍然存在强引用,垃圾回收器就无法对其进行垃圾回收。在安卓平台,泄漏 Context 对象问题尤其严重。这是因为像 Activity 这样的 Context 对象会引用大量很占用内存的对象,例如 View 层级,以及其他的资源。如果 Context 对象发生了内存泄漏,那它引用的所有对象都被泄漏了。安卓设备大多内存有限,如果发生了大量这样的内存泄漏,那内存将很快耗尽。

如果一个对象的合理生命周期没有清晰的定义,那判断逻辑上的内存泄漏将是一个见仁见智的问题。幸运的是,activity 有清晰的生命周期定义,使得我们可以很明确地判断 activity 对象是否被内存泄漏。onDestroy() 函数将在 activity 被销毁时调用,无论是程序员主动销毁 activity,还是系统为了回收内存而将其销毁。如果 onDestroy 执行完毕之后,activity 对象仍被 heap root 强引用,那垃圾回收器就无法将其回收。所以我们可以把生命周期结束之后仍被引用的 activity 定义为被泄漏的 activity。

Activity 是非常重量级的对象,所以我们应该极力避免妨碍系统对其进行回收。然而有多种方式会让我们无意间就泄露了 activity 对象。我们把可能导致 activity 泄漏的情况分为两类,一类是使用了进程全局(process-global)的静态变量,无论 APP 处于什么状态,都会一直存在,它们持有了对 activity 的强引用进而导致内存泄漏,另一类是生命周期长于 activity 的线程,它们忘记释放对 activity 的强引用进而导致内存泄漏。下面我们就来详细分析一下这些可能导致 activity 泄漏的情况。

1.静态 Activity

泄漏 activity 最简单的方法就是在 activity 类中定义一个 static 变量,并且将其指向一个运行中的 activity 实例。如果在 activity 的生命周期结束之前,没有清除这个引用,那它就会泄漏了。这是因为 activity(例如 MainActivity) 的类对象是静态的,一旦加载,就会在 APP 运行时一直常驻内存,因此如果类对象不卸载,其静态成员就不会被垃圾回收。
void setStaticActivity() {
  activity = this;
}

View saButton = findViewById(R.id.sa_button);
saButton.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View v) {
    setStaticActivity();
    nextActivity();
  }
});




内存泄漏场景 1 - Static Activity

2. 静态 View

另一种类似的情况是对经常启动的 activity 实现一个单例模式,让其常驻内存可以使它能够快速恢复状态。然而,就像前文所述,不遵循系统定义的 activity 生命周期是非常危险的,也是没必要的,所以我们应该极力避免。

但是如果我们有一个创建起来非常耗时的 View,在同一个 activity 不同的生命周期中都保持不变呢?所以让我们为它实现一个单例模式,就像这段代码。现在一旦 activity 被销毁,那我们就应该释放大部分的内存了。
void setStaticView() {
  view = findViewById(R.id.sv_button);
}

View svButton = findViewById(R.id.sv_button);
svButton.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View v) {
    setStaticView();
    nextActivity();
  }
});




内存泄漏场景 2 - Static View

内存泄漏了!因为一旦 view 被加入到界面中,它就会持有 context 的强引用,也就是我们的 activity。由于我们通过一个静态成员引用了这个 view,所以我们也就引用了 activity,因此 activity 就发生了泄漏。所以一定不要把加载的 view 赋值给静态变量,如果你真的需要,那一定要确保在 activity 销毁之前将其从 view 层级中移除。

3. 内部类

现在让我们在 activity 内部定义一个类,也就是内部类。这样做的原因有很多,比如增加封装性和可读性。如果我们创建了一个内部类的对象,并且通过静态变量持有了 activity 的引用,那也会发生 activity 泄漏。
void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createInnerClass();
        nextActivity();
    }
});




内存泄漏场景 3 - Inner Class

不幸的是,内部类能够引用外部类的成员这一优势,就是通过持有外部类的引用来实现的,而这正是 activity 泄漏的原因。

4. 匿名类

类似的,匿名类同样会持有定义它们的对象的引用。因此如果在 activity 内定义了一个匿名的 AsyncTask 对象,就有可能发生内存泄漏了。如果 activity 被销毁之后 AsyncTask 仍然在执行,那就会组织垃圾回收器回收 activity 对象,进而导致内存泄漏,直到执行结束才能回收 activity。
void startAsyncTask() {
    new AsyncTask<Void, Void, Void>() {
        @Override protected Void doInBackground(Void... params) {
            while(true);
        }
    }.execute();
}

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View aicButton = findViewById(R.id.at_button);
aicButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        startAsyncTask();
        nextActivity();
    }
});




内存泄漏场景 4 - AsyncTask

5. Handlers

同样的,定义一个匿名的 Runnable 对象并将其提交到 Handler 上也可能导致 activity 泄漏。Runnable 对象间接地引用了定义它的 activity 对象,而它会被提交到 Handler 的 MessageQueue 中,如果它在 activity 销毁时还没有被处理,那就会导致 activity 泄漏了。
void createHandler() {
    new Handler() {
        @Override public void handleMessage(Message message) {
            super.handleMessage(message);
        }
    }.postDelayed(new Runnable() {
        @Override public void run() {
            while(true);
        }
    }, Long.MAX_VALUE >> 1);
}


View hButton = findViewById(R.id.h_button);
hButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createHandler();
        nextActivity();
    }
});




内存泄漏场景 5 - Handler

6. Threads

同样的,使用Thread和TimerTask也可能导致activity泄漏。
void spawnThread() {
    new Thread() {
        @Override public void run() {
            while(true);
        }
    }.start();
}

View tButton = findViewById(R.id.t_button);
tButton.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View v) {
      spawnThread();
      nextActivity();
  }
});




内存泄漏场景 6 - Thread

7. Timer Tasks

只要它们是通过匿名类创建的,尽管它们在单独的线程被执行,它们也会持有对 activity 的强引用,进而导致内存泄漏。
void scheduleTimer() {
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            while(true);
        }
    }, Long.MAX_VALUE >> 1);
}

View ttButton = findViewById(R.id.tt_button);
ttButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        scheduleTimer();
        nextActivity();
    }
});




内存泄漏场景 7 - TimerTask

8. Sensor Manager

最后,系统服务可以通过 context.getSystemService 获取,它们负责执行某些后台任务,或者为硬件访问提供接口。如果 context 对象想要在服务内部的事件发生时被通知,那就需要把自己注册到服务的监听器中。然而,这会让服务持有 activity 的引用,如果程序员忘记在 activity 销毁时取消注册,那就会导致 activity 泄漏了。
void registerListener() {
       SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
       Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
       sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}

View smButton = findViewById(R.id.sm_button);
smButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        registerListener();
        nextActivity();
    }
});




内存泄漏场景 8 - Sensor Manager

现在,我们展示了八种很容易不经意间就泄漏大量内存的情景。请记住,最坏的情况下,你的 APP 可能会由于大量的内存泄漏而内存耗尽,进而闪退,但它并不总是这样。相反,内存泄漏会消耗大量的内存,但却不至于内存耗尽,这时,APP 会由于内存不够分配而频繁进行垃圾回收。垃圾回收是非常耗时的操作,会导致严重的卡顿。在 activity 内部创建对象时,一定要格外小心,并且要经常测试是否存在内存泄漏。
  • 大小: 73.6 KB
  • 大小: 106.7 KB
  • 大小: 90.4 KB
  • 大小: 74.6 KB
  • 大小: 125.7 KB
  • 大小: 74.6 KB
  • 大小: 74.6 KB
  • 大小: 74.9 KB
0
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 常见的八种导致 APP 内存泄漏的问题1

    1. 静态 Activity 3. 内部类 4. 匿名类

  • 常见的八种导致 APP 内存泄漏的问题(上)

    这减少了段错误(segmentation fault)导致的闪退,也减少了内存泄漏导致的堆空间膨胀,让编写的代码更加安全。然而,Java 中依然有可能发生内存泄漏。所以你的安卓 APP 依然有可能浪费了大量的内存,甚至由于内存...

  • 常见的八种导致 APP 内存泄漏的问题(下)

    Runnable 对象间接地引用了定义它的activity 对象,而它会被提交到 Handler 的 MessageQueue 中,如果它在 activity 销毁时还没有被处理,那就会导致 activity 泄漏了。 Threads 同样的,使...

  • Android常见的八种导致 APP 内存泄漏的问题

    这减少了段错误(segmentation fault)导致的闪退,也减少了内存泄漏导致的堆空间膨胀,让编写的代码更加安全。然而,Java 中依然有可能发生内存泄漏。所以你的安卓 APP 依然有可能浪费了大量的内存,甚至由于内存...

  • 基于OpenGL的C语言的魔方项目.zip

    C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

  • QT-qtablewidget表头添加复选框QHeaderView

    在 Qt 框架中,要在 QTableWidget的表头中添加复选框,可以通过继承 QHeaderView 并重写 paintSection 方法来实现。 介绍一种继承 QHeaderView的方法分别实现QTableWidget中添加复选框,可全选/全不选/部分选。

  • 分段划线测量表格通用版.doc

    分段划线测量表格通用版.doc

  • 扫雷小游戏(JAVA SE).zip

    该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

  • 啊哈算法哈磊基于队列的优化Bellman-ford算法搜索单源最短路径-Java实现

    本资源源自《啊哈算法》的高级算法实践,专注于讲解基于队列优化的Bellman-Ford算法在单源最短路径问题上的应用,并提供了Java语言的实现代码。哈磊老师以其独特的教学视角,不仅详细解析了传统Bellman-Ford算法的原理及其在处理含有负权边图中的优势,还深入介绍了如何通过队列优化(通常指SPFA算法)来加速这一过程,减少不必要的松弛操作,提高算法效率。 Java实现部分,代码实现清晰,注释详尽,从初始化距离数组、设置源节点开始,逐步展示如何维护一个队列来记录待检查的顶点,并通过队列进行有效的顶点遍历和松弛操作。本资源通过实例演示了如何高效地更新路径长度,检测负权环,并最终确定从源点到图中所有其他顶点的最短路径。 这份资源特别适合对图算法有深入学习需求的学生、工程师以及算法爱好者,尤其是那些关注算法性能优化和实际应用的人士。通过学习这份资源,你不仅能掌握Bellman-Ford算法的核心逻辑,还将理解如何通过队列优化策略提升算法的执行效率,为解决复杂的网络最短路径问题提供有力工具。在算法学习和软件开发的道路上,这将是一块宝贵的垫脚石,助你攀登更高的算法高峰。

  • 基于一阶倒立摆线性二次型最优控制

    基于一阶倒立摆线性二次型最优控制

  • Java课程设计,做个扫雷游戏.zip

    该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

  • 计算机毕业设计-asp网上体育用品商店的设计与实现(LW+源代码).zip

    计算机毕业设计资源包含(项目部署视频+源码+LW+开题报告等等),所有项目经过助教老师跑通,有任何问题可以私信博主解决,可以免费帮部署。

  • springboot学生考勤系统.rar

    基于Java的教师考勤管理系 统 成绩查看 请假申请 考勤管理 用户 注册登录 个人中心 系统管理 学生管理 管理员 老师管理 成绩管理 考勤信息管理 请假信息管理 个人中心 公告管理 个人中心 学生考勤管理 教师 学生请假申请管理 成绩管理 班级管理 公告管理

  • Delphi 12 控件之EhLib.v10.2.42 for Delphi 5-12.src.rar

    EhLib.v10.2.42 for Delphi 5-12.src.rar

  • chromedriver-win64_118.0.5957.0.zip

    chromedriver-win64_118.0.5957.0.zip

  • 基于Selenium的Java爬虫实战(内含谷歌浏览器Chrom和Chromedriver版本124.0.6350.0)

    资源包括: 1.Java爬虫实战代码 2.selenium学习笔记 3.代码演示视频 4.谷歌浏览器chrom124.0.6350.0 chrome-linux64.zip chrome-mac-arm64.zip chrome-mac-x64.zip chrome-win32.zip chrome-win64.zip 5.谷歌浏览器驱动器Chromedriver124.0.6350.0 chromedriver-linux64.zip chromedriver-mac-arm64.zip chromedriver-mac-x64.zip chromedriver-win32.zip chromedriver-win64.zip 特别说明:Chrome 为测试版(不会自动更新) 仅适用于自动测试。若要进行常规浏览,请使用可自动更新的标准版 Chrome。)

  • 【C/C++免费资源】无尽之剑V3.5

    无尽之剑3.5震撼来袭!自创游戏,完美还原我的世界细节! 更新日志 3.5:更新图签,优化战斗盔甲防御机制 3.0:更新附魔,更改合成机制 2.5:更新撸树功能 2.0:更新盔甲 1.5:更新工作台(合成)功能 1.0:更新打怪,挖矿功能 0.5:框架搞好了

  • 应届生单页简历模版15份

    【单页简历模版15份】 一份优秀的简历能为求职就业的过程做好铺垫。 简历模版 一份简历最关键的地方之一就是一个简历模板,选择一个好的简历模版最为关键。在选择的时候一定要选择比较清晰直观的模版。 有的反例就是:个人信息在左边一栏,总的来说,没有那么直观。 简历是不断迭代优化的,所以个人比较喜欢WORD版本的简历模版。当然,也有人喜欢用类似超级简历之类的软件网站来生成简历,有时候也是可以的。 简历一般不宜过长,要保证简洁,一般1到2页即可。一般一页最好,但如果个人亮点足够多(比如多段高含金量的实习经历),也可以扩展到2页,没有必要为了一页删减去自己的亮点。 一份合格的简历应该怎么写:https://blog.csdn.net/qq_33445788/article/details/139545096?spm=1001.2014.3001.5502 如何让简历如虎添翼,如何应对面试游刃有余? 解锁这几个秘技!【后端篇】:https://blog.csdn.net/qq_33445788/article/details/139691619?spm=1001.2014.3001.5502

  • 基于JSP+Servlet+JavaBean的超市进销存管理系统.zip

    该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

Global site tag (gtag.js) - Google Analytics