Android 面试题 内存泄露的原因 二

🔥 什么是内存泄漏 🔥

        在Android开发过程中,当一个对象已经不需要再使用了,本该被回收时,而另个正在使用的对象持有它引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了

        它是造成应用程序OOM的主要原因之一;由于Android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过系统分配的内存限额,这就造成了内存泄漏而导致应用Crash;

 🔥 内存泄漏排查 使用adb命令 🔥 

 使用adb命令 查看当前activity数量。不停打开关闭要排查页面,由于关闭页面后垃圾回收不会立即执行,为了测试,借助Android Studio自带的 Profiler,点击强制垃圾回收若activiy数量和最开始时一致,则表示正常,若activity数量增加,则表明内存泄漏。

adb shell dumpsys meminfo 包名

 

 🔥 内存泄漏排查 使用Profiler 🔥

使用AS中Profiler进一步问题排查,点击Dump Java heap导出堆分配。

🔥 静态Activity和View 造成内存泄漏 🔥

静态Activity(Activity上下文Context)和View

静态变量Activity和View会导致内存泄漏,在下面代码中对Activity的Context和TextView设置为静态对象,从而产生内存泄漏;
因为context和textView的实例的生命周和应用的生命一样,而他们持有当前Activity(MemoryTestActivity)的引用,一旦MemoryTestActivity销毁,而他的引用一直持有,就不会被回收,所以产生内存泄漏了;

public class MemoryTestActivity extends AppCompatActivity {private static Context context;private static TextView textView;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);context = this;textView = new TextView(this);}
}

 🔥 单例造成的内存泄漏 🔥

单例造成的内存泄漏

Android的单例模式是开发中经常使用的模式,使用不恰当可能导致内存泄漏;单例的生命周期和应用的生命周期一样,也就是单例持有必须是和应用生命周期一样的对象,不能持有和应用生命周期不一致的对象例如:Activity(Context)上下文:

public class TestManager {private static TestManager manager;private Context context;private TestManager(Context context) {this.context = context;}/*** 如果传入的context是activity,service的上下文,会导致内存泄漏* 原因是我们的manger是一个static的静态对象,这个对象的生命周期和整个app的生命周期一样长* 当activity销毁的时候,我们的这个manger仍然持有者这个activity的context,* 就会导致activity对象无法被释放回收,就导致了内存泄漏*/public static TestManager getInstance(Context context) {if (manager == null) {manager = new TestManager(context);}return manager;}
}

解决方法:

修改TestManager单例模式使用的上下文Context,TestManager单例模式引用ApplicationContext,TestManager单例模式和应用生命周期一样,ApplicationContext和应用的生命周期是一样,这样不会出现内存泄漏;

public class TestManager {private static TestManager manager;private Context context;private TestManager(Context context) {this.context = context;}//正确写法public static TestManager getInstance(Context context) {if (manager == null) {manager = new TestManager(context.getApplicationContext());}return manager;}
}

🔥 线程造成的内存泄漏 🔥

匿名线程内部类会隐式引用Activity,当执行耗时任务时,一直隐式引用Activity,当Activity关闭时,由于匿名线程内部类会隐式引用Activity无法及时回收;

public class MemoryTestActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState{super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);anonymousInnerClass();}//匿名内部类持有MemoryTestActivity实例引用,当耗时匿名线程内部类执行完成以后        //MemoryTestActivity实例才会回收;public void anonymousInnerClass() {new AsyncTask<Void, Void, Void>(){@Overrideprotected Void doInBackground(Void... voids) {//执行异步处理        SystemClock.sleep(120000);return null;}}.execute();}
}

解决方法:

修改AsyncTask匿名内部类为静态类,解除Activity隐式引用,MemoryTestActivity销毁时要及时取消异步任务staticAsyncTask.cancel(true),防止异步任务执行完成更新销毁MemoryTestActivity实例的UI;

public class MemoryTestActivity extends AppCompatActivity {private StaticAsyncTask staticAsyncTask;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);staticAsyncTask = new StaticAsyncTask(this);staticAsyncTask.execute();}private static class StaticAsyncTask extends AsyncTask<Void, Void, Void> {private WeakReference<Context> weakReference;public StaticAsyncTask(Context context) {weakReference = new WeakReference<Context>(context);}@Overrideprotected Void doInBackground(Void... voids) {//执行异步处理            SystemClock.sleep(120000);return null;}@Overrideprotected void onPostExecute(Void aVoid) {super.onPostExecute(aVoid);MemoryTestActivity activity = (MemoryTestActivity) weakReference.get();if (activity != null) {//异步任务执行完成,执行UI处理}}}@Overrideprotected void onDestroy() {super.onDestroy();staticAsyncTask.cancel(true);}
}

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

这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免重复创建,不过这种写法会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态实例,该实例的生命周期和应用一样长,这就导致了该静态实例一直持有该Activity的引用,导致Activity的内存资源不能正常回收; 

public class MemoryTestActivity extends AppCompatActivity {private static TestResource testResource;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);testResource = new TestResource();}class TestResource{//资源类}
}

 解决方法:

将该内部类设为静态内部类或将内部类抽象出来封装一个单例,如果需要使用Context,请使用ApplicationContext;

 🔥 Handler造成的内存泄漏 🔥

Handler的使用造成的内存泄漏问题是比较常见的,平时处理网络任务或者封装一些请求回调等api都应该会借助Handler处理,对于Handler的使用代码不规范可能会造成内存泄漏,如下示例:

private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//处理UI显示}
};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);loadData();}//loadData()方法在子线程中执行private void loadData() {Message message = Message.obtain();//模拟线程延迟120秒发送        MessagemHandler.sendMessageDelayed(message, 120000);}
}

这种创建Handler的方式可能造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时,消息队列还有未处理的消息或者正在处理的消息(例如上面的例子,子线程中处理耗时任务,还没有执行完毕,activity就退出销毁),而消息队列中Message持有mHandler实例引用,mHander又持有Activity的引用,所以导致Activity的内存无法及时回收,引发内存泄漏;

public class MemoryTestActivity extends AppCompatActivity {private Handler handler = new StaticHandler(this);private static class StaticHandler extends Handler {WeakReference<Context> weakReference;public StaticHandler(Context context) {weakReference = new WeakReference<>(context);}@Overridepublic void handleMessage(Message msg) {//处理UI显示MemoryTestActivity activity = (MemoryTestActivity) weakReference.get();if (activity != null) {}}
}@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);loadData();
}
//loadData()方法在子线程中执行
private void loadData() {Message message = Message.obtain();//模拟线程延迟120秒发送Messagehandler.sendMessageDelayed(message, 120000);
}
@Override
protected void onDestroy() {super.onDestroy();handler.removeCallbacksAndMessages(null);}
}

创建一个静态Handler内部类,然后对Handler持有的对象使用弱应用,这样在回收时也可以回收Handler持有的对象,这样避免了Activity泄漏,如果Handler被delay(延迟执行),在Activity的Destroy或者Stop时应该移除消息队列中的消息;
handler.removeCallbacksAndMessages(null);移除消息队列中所有的消息和线程;

解决方案一 : 通过程序逻辑来进行维护 

        在关闭Activity的时候停掉后台线程;线程停掉相当于切断了Handler和外部连接线,Activity自然会被在合适的时候回收;

        如果Handler被delay延迟的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行;

解决方案二 :  将Handler声明为静态类

        在Java中,非静态的内部类和匿名内部类都会隐式持有其外部类的引用,静态内部类不会持有外部类的引用。静态类不持有外部类的对象,所以你的Activity可以随意被回收;由于Handler不在持有外部类的对象的引用,导致程序不允许你在Handler中操作Activity中的对象了,所以你需要在Handler中增加一个对Activity的弱引用(WeakReference);

🔥 动画造成内存泄露 🔥

在属性动画中有一类无限循环动画,如果在Activity中播放这类动画并且在onDestroy()中没有去停止动画,那么动画会一直播放下去,这时候Activity会被View所持有,从而导致Activity无法被释放。解决此类问题要在onDestroy()方法中去调用objectAnimator.cancel()来停止动画;

public class MemoryTestActivity extends AppCompatActivity {private TextView textView;private ObjectAnimator objectAnimator;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_memory_test);textView = (TextView)this.findViewById(R.id.textView2);objectAnimator = ObjectAnimator.ofFloat(textView, "rotation", 0, 360);objectAnimator.setRepeatCount(ValueAnimator.INFINITE);objectAnimator.start();}@Overrideprotected void onDestroy() {super.onDestroy();}
}

由于未在onDestroy()方法中去调用objectAnimator.cancel()来停止动画,执行动画的View一直引用Activity,导致Activity无法销毁;

解决办法:

在onDestroy()方法中去调用objectAnimator.cancel()来停止动画;

 🔥 第三方库使用不当 造成内存泄露 🔥

1、对于EventBus,RxJava等一些第三方开源框架的使用,若是Activity销毁之前没有进行解除订阅会导致内存泄漏;
2、需要在生命周期相对注册与注销(onCreate->onDestory | onResume->onPause … )

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

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

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

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

相关文章

SQL-每日一题【626.换座位】

题目 表: Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例 1: 解题思路 前置知识 MySQL 的 MOD() 函数是取模运算的函数&#xff0c;它返回两个数相除…

【自用记录】常见的第三方接口加密签名方式(ASCll码字典序、URL键值对、 SHA-256加密、MD5加密)

案例1: 案例2: 以上第三方接口都用类似的加密签名方式,两者有类似的部分: 方案1的: $kdata = array(parkId=>$parkId,ts => $ts,serviceCode=>getParkingPaymentList,reqId => $reqId,plateNo => $car_code,//车牌 可为空pageIndex => 1,//第几页page…

无涯教程-jQuery - innerHeight( )方法函数

innerHeight()方法获取第一个匹配元素的内部高度(不包括边框&#xff0c;包括填充)。 innerHeight( ) - 语法 selector.innerHeight( ) innerHeight( ) - 示例 以下是一个简单的示例&#xff0c;简单说明了此方法的用法- <html><head><title>The jQuery…

适合做笔记的软件有哪些?8款好用强大的笔记软件推荐!

除了Goodnotes和Notability&#xff0c;你还知道哪些值得推荐的免费笔记软件吗&#xff1f;本文结合自己的使用经验&#xff0c;推荐笔记软件的同时&#xff0c;亦推荐一些不错的绘图软件供大家选择使用。 1.OneNote 基本的笔记功能都有&#xff0c;加粗、倾斜、下划线、突…

TPlink云路由器界面端口映射设置方法?快解析内网穿透能实现吗?

有很多网友在问&#xff1a;TPlink路由器端口映射怎么设置&#xff1f;因为不懂端口映射的原理&#xff0c;所以无从下手&#xff0c;下面小编就给大家分享TPlink云路由器界面端口映射设置方法&#xff0c;帮助大家快速入门TP路由器端口映射设置方法。 1.登录路由器管理界面&a…

高等数学中如何求间断点

高等数学中求间断点是一项重要的技巧&#xff0c;特别适用于分析函数的性质和图像的特征。在本文中&#xff0c;我们将深入探讨如何在给定函数中找到间断点&#xff0c;并解释其数学原理和实际应用。 什么是间断点&#xff1f; 在高等数学中&#xff0c;间断点是指函数在某个点…

【C++从0到王者】第十四站:list基本使用及其介绍

文章目录 一、list基本介绍二、list基本使用1.尾插头插接口使用2.insert接口使用3.查找某个值所在的位置4.erase接口使用以及迭代器失效5.reverse6.sort7.merge8.unique9.remove11.splice 三、list基本使用完整代码 一、list基本介绍 如下所示&#xff0c;是库里面对list的基本…

pytorch学习——正则化技术——权重衰减

一、概念介绍 权重衰减&#xff08;Weight Decay&#xff09;是一种常用的正则化技术&#xff0c;它通过在损失函数中添加一个惩罚项来限制模型的复杂度&#xff0c;从而防止过拟合。 在训练参数化机器学习模型时&#xff0c; 权重衰减&#xff08;weight decay&#xff09;是…

消防应急照明设置要求在炼钢车间电气室的应用

摘 要:文章以GB51309—2018《消防应急照明和疏散指示系统技术标准》为设计依据&#xff0c;结合某炼钢车间转炉项目的设计过程&#xff0c;在炼钢车间电气室的疏散照明和备用照明的设计思路、原则和方法等方面进行阐述。通过选择合理的消防应急疏散照明控制系统及灯具供配电方案…

国内办公协作系统评测:5 款软件推荐

办公协作系统是现代信息化办公的必备工具之一&#xff0c;对于企业来说&#xff0c;选择一款好用的办公协作系统非常重要。然而&#xff0c;在众多的办公协作系统中&#xff0c;哪个好用是一个让人头痛的问题。总体而言&#xff0c;国内的办公协作系统已经相当成熟和完善&#…

golang interface类型的nil

golang中interface变量&#xff0c;底层两个对象来存&#xff0c;一个是type、一个是value&#xff0c;只有type、value都为nil时&#xff0c;interface变量才是nil package mainimport ("fmt""reflect" )type People interface {Show() }type Student str…

ALLEGRO之Logic

本文主要讲述ALLEGRO的Logic菜单。 &#xff08;1&#xff09;Net Logic&#xff1a;暂不清楚&#xff1b; &#xff08;2&#xff09;Net Schedule&#xff1a;暂不清楚&#xff1b; &#xff08;3&#xff09;AssignDifferential Pair&#xff1a;暂不清楚&#xff1b; &a…

layui框架学习(35:数据表格_列参数设置)

Layui中的table数据表格模块支持对表格及列进行基础参数设置以提高数据的可视化及可操作性&#xff0c;本文学习并记录与列相关的主要基础参数的用法及效果。   基础参数field设置待显示到列中的数据的字段名&#xff0c;主要针对数据表格url属性中返回的数据集合或data属性设…

《向量数据库指南》——Milvus Cloud 2.3 和 2.4 版本的重要变化

Milvus Cloud2.3 和 2.4 版本的重要变化。 首先是 Milvus Cloud2.3 将支持 Json 数据类型,在此基础上亦会支持 Schemaless。此前,用户在使用 Milvus Cloud的过程中会先定一个静态 Schema,此时,如果在实际业务层面如果多了几个 feature 或者 Metadata,就意味着数据需要重新…

echarts柱状图横坐标文字过长的解决办法

背景&#xff1a;echarts图中横坐标显示的文字过长&#xff0c;导致字都堆积在一块如下图所示 解决办法 一&#xff1a;可以尝试修改‘axisLabel’的‘rotate’和‘interval’参数&#xff0c;‘rotate’参数可以设置标签的旋转角度&#xff0c;可以避免标签之间的重叠&#x…

uni-app之微信小程序实现‘下载+保存至本地+预览’功能

目录 一、H5如何实现下载功能 二、微信小程序实现下载资源功能方面与H5有很大的不同 三、 微信小程序实现文件&#xff08;doc,pdf等格式&#xff0c;非图片&#xff09;下载&#xff08;下载->保存->预览&#xff09;功能 四、图片预览、保存、转发、收藏&#xff1…

Stephen Wolfram:神经网络

Neural Nets 神经网络 OK, so how do our typical models for tasks like image recognition actually work? The most popular—and successful—current approach uses neural nets. Invented—in a form remarkably close to their use today—in the 1940s, neural nets …

django自定义app,创建子应用

1.工程里创建apps包 &#xff1b; 2.创建子应用&#xff0c;pycharm terminal 运行&#xff1a;python ./nanage.py startapp app名称&#xff1b; 3.子应用移动到apps包里&#xff1b; 4.settings.py里设置INSTALLED_APPS如“apps.users”&#xff0c;该名字跟子应用apps.py文…

红宝石阅读笔记

第七章 迭代器与生成器 7.3 生成器 乍一看理解&#xff0c;仔细想没理解&#xff0c;然后自己让n2&#xff0c;还原nTimes&#xff0c;等价于 function* nTimes() {if (true) {yield* (function* A() {if (true) {yield* (function* B() { })();yield 0;}})();yield 1;} } 最…

NO1.使用命令行创建Maven工程

①在工作空间目录下打开命令窗口 ②使用命令行生成Maven工程 mvn archetype:generate 运行 MVN 原型&#xff1a;生成命令,下面根据提示操作 选择一个数字或应用过滤器&#xff08;格式&#xff1a;[groupId&#xff1a;]artifactId&#xff0c;区分大小写包含&#xff09;&a…