内存溢出、内存泄漏与内存抖动

文章目录

  • 前言
  • 一、内存溢出
  • 二、内存泄漏
  • 三、内存抖动
  • 四、会引发内存泄漏的情况
    • 1、单例引起的内存泄漏
    • 2、非静态内部类创建静态实例引起的内存泄漏
    • 3、Handler 引起的内存泄漏
    • 4、WebView引起的内存泄露
    • 5、Asynctask引起的内存泄露
    • 6、资源对象未关闭引起的内存泄露
    • 7、其他一些

前言

OOM产生的原因:1.内存泄漏 2.频繁申请内存得不到及时的回收
减少OOM的概率:1.尽可能少的发生内存泄漏 2.尽可能不在循环中申请内存 3.尽可能不在调用次数多的函数中申请内存

一、内存溢出

内存溢出(Out Of Memory) 就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。

二、内存泄漏

内存泄漏(memory leak):指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况

原因:一个长生命周期的对象持有一个短生命周期对象的引用
通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM

三、内存抖动

内存抖动(Memory Churn):在短时间内反复地发生内存增长和回收

问题:内存抖动可能导致程序卡顿甚至OOM内存溢出。

卡顿原因:当gc回收内存时,会使当前的工作线程暂停,如果多次内存回收行为集中在短时间内爆发,就有可能造成卡顿

gc检测垃圾对象主要有两种算法:引用计数法和可达性分析法

可以通过下面的代码看出每个应用程序最高可用内存是多少:

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024 /1024); 
Log.d("TAG", "Max memory is " + maxMemory+"MB");

四、会引发内存泄漏的情况

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;}
}

我们来分析问题所在:
从上面的代码我们可以看出,在创建单例对象的时候,引入了一个 Context 上下文对象,如果我们把Activity注入进来,会导致这个 Activity 一直被单例对象持有引用,当这个 Activity 销毁的时候,对象也是没有办法被回收的。

解决方案 :
在这里我们只需要让这个上下文对象指向应用的上下文即可
this.context = context.getApplicationContext() ,因为应用的上下文对象的生命周期和整个
应用一样长。

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

由于非静态内部类会默认持有外部类的引用,如果我们在外部类中去创建这个内部类对象,当频繁打开关闭Activity,会导致重复创建对象,造成资源的浪费,为了避免这个问题我们一般会把这个实例设置为静态,这样虽然解决了重复创建实例,但是会引发出另一个问题,就是静态成员变量它的生命周期是和应用的生命周期一样长的,然而这个静态成员变量又持有该Activity的引用,所以导致这个Activity销毁的时候,对象也是无法被回收的。

public class MainActivity extends AppCompatActivity {private static TestResource mResource = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (mResource == null) {mResource = new TestResource();}//...}class TestResource {//...}
}

我们来分析问题所在:其实这个和上面单例对象的内容泄漏问题是一样的,由于静态对象持有Activity的引用,导致Activity没办法被回收。

采用的解决方案:
在这里我们只需要把非静态内部类改成静态内部类即可( static class TestResource )。

3、Handler 引起的内存泄漏

原因:首先我们知道程序启动时在主线程中会创建一个Looper对象,这个 Looper 里维护着一个 MessageQueue 消息队列,这个消息队列里会按时间顺序存放着Message ,然后上面的 Handler 是通过内部类来创建的,内部类会持有外部类的引用,也就是 Handler持有Activity 的引用,而消息队列中的消息 target 是指向 Handler 的,也就等同消息持有 Handler的引用, 也就是说当消息队列中的消息如果还没有处理完,这些未处理的消息(也可以理解成延迟操作)是持有 Activity 的引用的,此时如果关闭 Activity ,是没办法回收的,从而就会导致内存泄露。

正确写法:
先把非静态内部类改成静态内部类(如果是 Runnable 类也需要改成静态),然后在Activity 的 onDestroy 中移除对应的消息,再来需要在 Handler 内部用弱引用持有 Activity ,因为让内部类不再持有外部类的引用时,程序也就不允许Handler 操作 Activity 对象了。

   MyHandler myHandler = new MyHandler(this);@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);new Thread(new Runnable() {@Overridepublic void run() {myHandler.sendMessage(Message.obtain());}}).start();}@Overrideprotected void onDestroy() {super.onDestroy();//移除对应的Runnable或者是Message// mHandler.removeCallbacks(runnable);// mHandler.removeMessages(what);mHandler.removeCallbacksAndMessages(null);}private static class MyHandler extends Handler {private WeakReference<Activity> mActivity;public MyHandler(Activity activity) {mActivity = new WeakReference<Activity>(activity);}@Overridepublic void handleMessage(Message msg) {if (mActivity.get() == null) {return;}//to do something.. }
};

4、WebView引起的内存泄露

关于WebView的内存泄漏,这是个绝对的大大大大大坑!不同版本都存在着不同版本的问题,这里我只能给出我平时的处理方法,可能不同机型上存在的差异,只能靠积累了。

方法一:
首先不要在 xml 去定义,定义一个 ViewGroup 就行,然后动态在代码中 new WebView(Context
context) (传入的 Context 采取弱引用),再通过 addView 添加到 ViewGroup 中,最后在页面销毁执行 onDestroy()的时候把 WebView 移除。
方法二:
简单粗暴,直接为 WebView 新开辟一个进程,在结束操作的时候直接 System.exit(0) 结束掉进程,这里需要注意进程间的通讯,可以采取Aidl , Messager , Content Provider , Broadcast 等方式。

5、Asynctask引起的内存泄露

这部分和 Handler 比较像,其实也是因为内部类持有外部类引用,一样的改成静态内部类,然后在onDestory 方法中取消任务即可。

6、资源对象未关闭引起的内存泄露

这块就比较简单了,比如我们经常使用的广播接收者,数据库的游标,多媒体,文档,套接字等。

7、其他一些

还有一些需要注意的,比如注册了 EventBus 没注销,添加 Activity 到栈中,销毁的时候没移除等。

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

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

相关文章

2023年【天津市安全员C证】模拟考试及天津市安全员C证实操考试视频

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 天津市安全员C证模拟考试是安全生产模拟考试一点通生成的&#xff0c;天津市安全员C证证模拟考试题库是根据天津市安全员C证最新版教材汇编出天津市安全员C证仿真模拟考试。2023年【天津市安全员C证】模拟考试及天津市…

云上攻防-云原生篇KubernetesK8s安全APIKubelet未授权访问容器执行

文章目录 K8S集群架构解释K8S集群攻击点-重点API Server未授权访问&kubelet未授权访问复现k8s集群环境搭建1、攻击8080端口&#xff1a;API Server未授权访问2、攻击6443端口&#xff1a;API Server未授权访问3、攻击10250端口&#xff1a;kubelet未授权访问 K8S集群架构解…

QT中使用QVTKOpenGLNativeWidget的简单教程以及案例

先添加一个带有ui的QT应用程序。 一、在ui界面中添加QVTKOpenGLNativeWidget控件 先拖出来一个QOpenGLWidget控件 修改布局如下&#xff1a; 然后将QOpenGLWidget控件提升为QVTKOpenGLNativeWidget控件&#xff0c;步骤如下&#xff1a; 右击QOpenGLWidget窗口&#xff0c;选…

springboot配置swagger

springboot配置swagger Swagger 是什么Swagger配置springboot代码展示总结 Swagger 是什么 Swagger 是一个用于构建、文档和调用 RESTful Web 服务的强大工具。它提供了以下几方面的好处&#xff1a; 自动生成 API 文档: Swagger 可以自动生成 API 文档&#xff0c;包括接口的…

【Golang】grpc环境踩的坑

关于’protoc-gen-go’ 不是内部或外部命令 这个问题的出现是因为没有这个文件导致的 这个文件要通过我们下载的google.golang.org这个文件编译生成的 这里建议下载google提供的grpc包 protobuf的源码&#xff1a; git clone https://github.com/golang/protobuf 下载好之后进…

用go封装一下二级认证功能

用go封装一下二级认证 本篇为用go设计开发一个自己的轻量级登录库/框架吧 - 秋玻 - 博客园 (cnblogs.com)的二级认证业务篇&#xff0c;会讲讲二级认证业务的实现&#xff0c;给库/框架增加新的功能。 源码&#xff1a;github.com/weloe/token… 在一个系统中&#xff0c;为了…

win11 定时计划任务

控制面板 任务计划 添加任务计划 &#xff0c;选按步骤添加。

谷歌浏览器访问127.0.0.1时报错 Failed to read the ‘sessionStorage‘ property from ‘Window‘

谷歌浏览器访问 127.0.0.1 时报错如下&#xff1a; Uncaught DOMException: Failed to read the ‘sessionStorage’ property from ‘Window’: Access is denied for this document. 原因&#xff1a; 谷歌浏览器设置中禁止了 127.0.0.1 存储数据到浏览器设备上 解决方法…

NewStarCTF2023公开赛道-压缩包们

题目提示是压缩包 用010editor打开&#xff0c;不见PK头&#xff0c;补上50 4B 03 04 14 00 00 00 将文件改成.zip后缀&#xff0c;打开&#xff0c;解压出flag.zip 尝试解压&#xff0c;报错 发现一串base64编码 SSBsaWtlIHNpeC1kaWdpdCBudW1iZXJzIGJlY2F1c2UgdGhleSBhcmUgd…

metaRTC7集成lvgl ui demo编译指南

概要 开源轻量级嵌入式图形库lvgl:Light and Versatile Graphics Library&#xff0c;最低只需8kb内存&#xff0c;可为任何 MCU、MPU 和显示类型创建漂亮的 UI。 metaRTC新增lvgl demo&#xff0c;可在linux下编译运行。 源码下载 https://github.com/metartc/metaRTC/rel…

安卓程序执行入口

Android程序执行入口 Android应用程序的执行入口是在一个特定的 Java 类中&#xff0c;通常是 MainActivity 或 SplashActivity&#xff0c;具体取决于应用的设计和结构。 Android应用程序的执行入口通常通过以下方式进行定义&#xff1a; 在 AndroidManifest.xml 文件中&am…

@Component在类上构造器注入无法注入

Component注解是Spring框架提供的一个注解&#xff0c;用于标识一个类为组件&#xff0c;并让Spring自动进行组件扫描和实例化。在类上使用Component注解时&#xff0c;并不能实现构造器注入。 如果你希望在类中进行构造器注入&#xff0c;可以使用其他注解来实现&#xff0c;…

一图看懂CodeArts Governance 三大特性,带你玩转开源治理服务

华为云开源治理服务CodeArts Governance是针对软件研发提供的一站式开源软件治理服务&#xff0c;凝聚华为在开源治理上的优秀实践经验&#xff0c;提供开源软件元数据及软件成分分析、恶意代码检测等能力&#xff0c;从合法合规、网络安全、供应安全等维度消减开源软件使用风险…

【分享Python代码】图片转化为素描画

哈喽&#xff0c;大家好&#xff0c;我是木易巷~ 代码生成效果图 原图&#xff1a; 生成图&#xff1a; 原图&#xff1a; 生成图&#xff1a; 准备工作 Python编程首先需要安装环境&#xff0c;下面是详细步骤&#xff1a; 会的小伙伴可自行跳过&#xff0c;代码在最后 1…

4大软件测试策略的特点和区别(单元测试、集成测试、确认测试和系统测试)

四大软件测试策略分别是单元测试、集成测试、确认测试和系统测试。 一、单元测试 单元测试也称为模块测试&#xff0c;它针对软件中的最小单元&#xff08;如函数、方法、类、模块等&#xff09;进行测试&#xff0c;以验证其是否符合预期的行为和结果。单元测试通常由开发人…

医疗制药行业数字化创新实践

本文将为大家分享3个制药行业的创新案例吧&#xff0c;都是在不同智能制造落地场景下的典型案例&#xff0c;希望对大家有所启发。 01 医疗设备企业零代码搭建集成式信息化管理平台&#xff0c;年节省150余万元 医疗制药行业数字化实现工具>>>>https://www.jianda…

C/C++笔试易错题+图解知识点(二)—— C++部分(持续更新中)

目录 1.构造函数初始化列表 1.1 构造函数初始化列表与函数体内初始化区别 1.2 必须在初始化列表初始化的成员 2. 引用初始化以后不能被改变&#xff0c;指针可以改变所指的对象 3 构造函数与析构函数系列题 3.1构造函数与析构函数的调用次数 4 类的运算符重载 5 类的静态数据成…

MySQL配置环境变量和启动登录

如果不配置环境变量&#xff0c;每次登录 MySQL 服务器时就必须进入到 MySQL 的 bin 目录下&#xff0c;也就是输入“cd C:\Program Files\MySQL\MySQL Server 5.7\bin”命令后&#xff0c;才能使用 MySQL 等其它命令工具&#xff0c;这样比较麻烦。配置环境变量以后就可以在任…

c# WPF 应用程序在屏幕上居中显示

xaml<Window ... WindowStartupLocation"CenterScreen">

vue小写数字转大写-例如:11转为十一

vue小写数字转大写-例如&#xff1a;11转为十一 在Vue中&#xff0c;可以使用自定义过滤器&#xff08;Custom Filter&#xff09;来将数字转换为大写的形式。 下面是一个示例&#xff1a; // main.js import Vue from vue;Vue.filter(toChineseNumber, function (value) {c…