Android性能优化——内存泄漏的根本原因

news2024/9/29 17:33:17

什么是内存泄露

什么是内存泄露,通俗的来说就是堆中的一些对象已经不会再被使用了,但垃圾收集器却无法将它们从内存中清除。

内存泄漏很严重的问题,因为它会阻塞内存资源并随着时间的推移降低系统性能。如果不进行有效的处理,最终的结果将会使应用程序耗尽内存资源,无法正常服务,导致程序崩溃,抛出java.lang.OutOfMemoryError异常。

堆内存中通常有两种类型的对象:被引用的对象和未被引用的对象。被引用的对象是应用程序中仍然具有活跃的引用,而未被引用的对象则没有任何活跃的引用。

垃圾收集器会回收那些未被引用的对象,但不会回收那些还在被引用的对象。这也是内存泄露发生的源头。

哪些操作会造成内存泄漏?

下面我们介绍几种常见的造成内存泄露的情况

1、意外声明全局变量是最常见也最容易修复的内存泄漏问题,比如:

function fn() {
    name = '张三';
}

解释器在解释上面的函数时,会把name当做全局变量,即window.name = ‘张三’。只要window对象没有被清理,那么name属性和属性值将一直存在,造成内存泄露。

解决方法:

(1)只要在变量声明前面加上var、let或const关键字即可,这样变量就会在函数执行完毕后离开作用域。

(2)使用this关键字

function fn() {
    this.name = '张三';
}

(3)可以在 JavaScript 文件开头添加 “use strict”,使用严格模式。这样在严格模式下解析 JavaScript 可以防止意外的全局变量

(4)在使用完之后,对其赋值为null或者重新分配

2、 定时器导致的泄露

let name = '张三';
setInterval(() => {
    console.log(name);
}, 100);

上面的代码中,只要定时器一直运行,回调函数中引用的name就会一直占用内存。

3、闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环),下面我们看一个JavaScript闭包导致的内训泄露例子

let fun = function() {
    let name = '张三';
    return function() {
        return name;
    };
};

调用fun()会导致分配给name的内存被泄漏。以上代码执行后创建了一个内部闭包,只要返回的函数存在就不能清理name,因为闭包一直在引用着它。

常见内存泄露问题

1.资源性对象未关闭

资源性对象(如Cursor、File等一些Closeable对象),它们往往使用了缓冲区,缓冲区不仅在JVM内,JVM之外也有。如果仅仅把变量设置为null,而不关闭它们,缓冲区得不到释放,往往造成内存泄露。

解决方案:一般在finally中关闭资源型对象,而后设置对象为null

2.注册对象未注销

订阅者模式中,如果注册对象不再使用时,未及时注销,会导致订阅者列表中维持这对象的引用,阻止垃圾回收,导致内存泄露。常见场景:动态注册BroadcastReceiver,注册PhoneStateListener,注册EventBus等等,

还有自定义使用订阅者模式的情形。

解决方案:一般在onDestroy()中进行解注册

3.非静态内部类的静态实例

非静态内部类持有外部类实例的引用,若非静态内部类的实例是静态的,便拥有app存活期整个生命周期,长期持有外部类的引用,阻止外部类实例被回收。

使用内部类的情况十分常见,尤其是匿名内部类:一些接口的匿名实现类,都是内部类。

解决方案:(1)改为静态内部类,不再持有外部类实例的引用 (2)避免申明非静态内部类的静态实例 (3)将内部类抽取出来封装成一个单例,如果需要Context,没有特殊要求就使用Application Context;如果需要Activity Context,则使用完毕置空,或者使用弱引用

4.单例模式引起的内存泄露

由于单例模式的静态特性,使得它的生命周期和我们的应用一样长,如果让单例无限制的持有Activity的强引用就会导致内存泄漏

解决方案:使用Activity的弱引用,或者没特殊需求时使用Application Context

5.Handler临时性内存泄露

非静态Handler持有Activity或Service的引用,Message中的target指向Handler实例,所以当Message在MessageQueue中排队,长时间未得到处理时,Activity边不会被回收,导致临时性内存泄露。

解决方案:(1)使用静态Handler内部类,然后对Handler持有的对象(Activity或Service)使用弱引用 (2)在onDestroy()中移除消息队列中的消息 mHandler.removeCallbacksAndMessages(null)

类似的:AsyncTask内部也是Handler机制,也存在同样的临时性内存泄露风险

6.容器中对象未及时清理导致内存泄露

容器类一般拥有较长的生命周期,若内部不再使用的对象不及时清理,内部对象边一直被容器类引用。上述2中的订阅者列表也属于容器类这中情况。另外常见的容器类还有线程池、对象池、图片缓存池等。线程池中的线程若存在ThreadLocal对象,因为线程对象一直被循环使用,ThreadLocal对象便会一直被引用,要注意对value对象的置空释放。

7.静态View导致内存泄露

有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收。 解决办法:在使用静态View时,需要确保在资源回收时,将静态View detach掉。

8.属性动画未及时关闭导致内存泄露

在使用ValueAnimator或者ObjectAnimator时,如果没有及时做cancel取消动画,就可能造成内存泄露。 因为在cancel方法里,最后调用了endAnimation(); ,在endAnimation里,有个AnimationHandler的单例,会持有属性动画对象的引用

解决办法:在在onDestory时,调用动画的cancel方法

9.WebView内存泄露

目前Android中WebView的实现存在很大的兼容性问题,Google支持各个ROM厂商自行定制自己的WebView实现,各个ROM间差异较大,且大多都存在内存泄露问题。除了调用其内部的clearCache()、clearHistory()、removeAllViews()、freeMemory()、destroy()和置null以外,一般比较粗暴有效的解决方法是:将包含WebView的Activity放在一个单独的进程中,不需要时将进程销毁,从而释放所有所占内存。

10.其他的系统控件以及自定义View

在 Android Lollipop 之前使用 AlertDialog 可能会导致内存泄漏

view中有线程或者动画 要及时停止。这是为了防止内存泄漏,可以在onDetachedFromWindow方法中结束,这个方法回调的时机是 当View的Activity退出或者当前View被移除的时候 会调用 这时候是结束动画或者线程的好时机 另外还有一个对应的方法 onAttachedToWindow 这个方法调用的时机是在包含View的Activity启动时 回调 回调在onDraw方法之前

11.其他常见的引起内存泄漏原因

  • (1)构造Adapter时,没有使用缓存的 contentView
  • (2)Bitmap在不使用的时候没有使用recycle()释放内存
  • (3)警惕线程未终止造成的内存泄露;譬如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity时切记结束线程。一个典型的例子就是HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周期,我们必须手动在Activity的销毁方法中调用thread.getLooper().quit();才不会泄露
  • (4)避免代码设计模式的错误造成内存泄露;譬如循环引用,A持有B,B持有C,C持有A,这样的设计谁都得不到释放

以上就是Android开发当中的性能优化重要部分;内存泄漏在开发中占比很重要,也是一个老生常谈的问题。除此之外还有一系列的Android性能优化学习。比喻:卡顿优化、内存优化、启动优化等等。这里想学习性能优化的推荐参考《Android性能优化手册》里面记录了7大块性能优化学习。

文末

理解内存泄漏的危害,我们举个简单的例子。有一个宾馆,有100间房间,顾客每次都是在前台进行登记,然后拿到房间钥匙。如果有些顾客不需要该房间了,也不归还钥匙,久而久之,前台处可用房间越来越少,收入也越来越少,濒临倒闭。当程序申请了内存,而不进行归还,久而久之,可用内存越来越少,OS就会进行自我保护,杀掉该进程,这就是我们常说的OOM(out of memory)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CS5518设计MIPI转LVDS转换方案/MIPI转LVDS方案芯片

CS5518是一个MIPI DSI输入,LVDS输出转换芯片。MIPI DSI最多支持4个车道,每个车道的最大运行速度 为1Gbps。LVDS支持18或24位像素,25MHz到154MHz,通过VESA或JEIDA格式。它只能使用一个1.8V电源,节省 成本,优…

Linux下的目录结构Linux基本命令(程序员必学)

目录 1.Linux下的目录结构 2.Linux的常用命令 2.1.# 与 $ 提示的区别 2.2.ifconfig 查看ip地址 2.3.su 命令格式 2.4.cd 2.5. 目录查看 2.6. 查看文件内容 2.7.创建目录及文件 2.8.复制和移动 2.9.其他 3. vim一般使用 1.Linux下的目录结构 /usr全称&#xff08…

Live Home 3D Pro - 用于公寓和房屋的室内设计,支持 3D 实时渲染

Live Home 3D Pro - 用于公寓和房屋的室内设计,支持 3D 实时渲染 Live Home 3D Pro是一个直观的应用程序,用于公寓和房屋的室内设计,以及几乎任何复杂的景观。专业版提供了一套扩展的工具和独特的出口质量。 下载 ► Live Home 3D Pro 下载安…

Java反射专题——基础知识一

反射 反射机制 java Reflection 反射机制允许程序在执行期借助于Reflection Api取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。加载完类之…

我眼中的代理模式

一.什么是代理模式? 举个简单的例子就是比如你相亲的女孩想跟你要个10W彩礼,但是她不好意思直接跟你说啊。这时候就媒婆就说我去跟小伙子说。有什么回应我告诉你。然后媒婆就先夸女方怎么怎么优秀,然后落到中心思想要钱。这里面相亲的女孩子就…

PL点亮LED灯

PL点亮LED灯 LED硬件 开发板在PL端给了一个LED灯,电压给低的时候才导通,才亮。然后需要看原理图确定具体是用的PL的哪个管脚以便进行控制。 Vivado 工程创建 打开创建新工程选择工程名和工程目录(不能有中文路径,也不能太长&…

什么品牌的蓝牙耳机音质最好?高品质蓝牙耳机排行榜

近年来,大量的蓝牙耳机品牌厂商涌入市场,蓝牙耳机的需求不断提高,但质量一直是人们担心的问题,今天就为大家盘点几款当前音质表现不错的真无线蓝牙耳机。 一、南卡小音舱蓝牙耳机 发声单元:13.3mm 网友推荐系数&…

hex文件格式剖析,以及hex与bin文件互相转换

大家好,我是学电子的小白白。 熟悉单片机开发的朋友,应该经常见到*.hex后缀的文件,它是单片机和嵌入式工程编译输出的一种常见的目标文件格式(比如keil就能编译输出hex文件),通过烧写工具把它下载到单片机…

【SpringBoot项目实战】图片压缩包上传、解压、存储等等一套流程教学

【SpringBoot项目实战】图片压缩包上传、解压、存储等等一套流程教学前言一、压缩包上传1、接口实现2、获取压缩包的文件名和文件路径二、压缩包解压并保存1、处理压缩包文件方法解压缩步骤:2、接口中实现处理压缩包三、总结前言 之前一直用的zip4j来对压缩包进行操…

开源项目推荐 | 中科院自动化所历时9年打造的类脑认知智能引擎“智脉”正式开源部署至OpenI启智社区

​人脑能够自组织地协同数百项认知功能,灵活适应复杂多变的环境。如何整合多尺度生物可塑性法则来构建具有生物合理性和计算高效性的神经网络模型是类脑人工智能和计算神经科学领域共同关注和面临的重要挑战。 中国科学院自动化研究所类脑认知智能研究组历时9年&am…

通关算法题之 ⌈动态规划⌋

动态规划 动态规划是什么?解决动态规划问题有什么技巧?如何学习动态规划? 首先,动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法,只不过在计算机问题上应用比较多,比如说让你求…

蓝桥杯寒假集训第二天(分巧克力)

没有白走的路,每一步都算数🎈🎈🎈 题目描述: 有很多的巧克力块,需要设计一个程序,让手艺师傅切出来的巧克力既满足切出来的巧克力的份数达到客户要求的份数,并且切出来的巧克力块尽…

硬盘图片丢失怎么办?看看这常见的三种恢复方法

电脑硬盘能够帮助我们存储各种各样的数据,比如图片数据,而随着这些充满回忆的图片积累的越来越多,难免会手误删除或其他原因导致丢失,那么对于电脑硬盘上丢失的图片如何恢复呢?在这里你可以了解到硬盘图片丢失常见原因…

python—subprocess模块常用方法介绍

目录 subprocess.run() subprocess.Popen() subprocess.call() subprocess.check_call() subprocess.getstatusoutput() subprocess.getoutput() subprocess.check_output() subprocess是子流程,即进程的意思,该模块可以启动一个新进程&#xff0…

印度电线标准IS 694(R2020),印度插头标准IS 1293(R2020)

印度电线标准IS 694(R2020) https://download.csdn.net/download/std86021/87328675POLYVINYLCHLORIDEINSULATEDUNSHEATHEDANDSHEATH更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/std86021/87328675 补丁一:IS694Amd…

已获1000 万美元战略融资,Web3链游#Delysium 有何魔力?

这周社区推文关注了AIGC在游戏领域中的创新应用,在Web3领域中,有哪些值得关注的链游项目呢?Mixlab小杜AIGC 与游戏机制创新玩法结合已成为潮流趋势,如游戏内容资产生成、智能NPC设计与自定义游戏玩法等都有非常大的探索空间。Web3…

【ACWING】【BFS】【844走迷宫】【845八数码】

一、走迷宫 给定一个 nm的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0表示可以走的路,1 表示不可通过的墙壁。 最初,有一个人位于左上角 (1,1)处,已知该人每次可以向上、下、左、右任意一…

CVE复现1

1 CVE-2022-22980 spring Data MongoDB SpEL 表达式注入,Spring Data MongoDB应用程序在对包含查询参数占位符的SpEL表达式使用Query或Aggregation注解的查询方法进行值绑定时,如果输入未被过滤,则容易受到SpEL注入攻击。 影响范围 Spring Data Mongo…

190:vue+openlayers 调整卫星运动的播放速度,展示运动轨迹

第190个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中利用两行根数计算卫星在某一时刻的位置点,利用时间循环,定位出不同的位置,呈现动画的效果。文中利用slider的改变来确定当前时间为开始时间,slider的值为加速倍数,每个setTimeout为10毫秒,更新…

【博客系统】前端页面

博客系统 实现一个简单的博客系统。 当前先完成页面设计部分,通过学习的前端知识来构建出网页。 主要分成四个部分: 博客列表页博客正文页博客登陆页博客编辑页 预期效果 博客列表页效果 博客详情页效果 博客登陆页效果 博客编辑页效果 实现博客列…