Android内存泄漏总结和性能优化技巧

       我们在开发安卓应用时,性能优化是非常重要的方面。一方面,优化可以提高应用的响应速度、降低卡顿率和提升应用流畅度,从而提升用户体验;另一方面,优化也可以减少应用的资源占用,提高应用的稳定性和安全性,降低应用被杀死的概率,从而提高用户的满意度和留存率。

      但是,对于许多开发者来说,安卓性能优化往往是一个比较棘手的问题。由于安卓设备的种类繁多,硬件配置各不相同,因此优化的方法和策略也各不相同。同时,安卓应用的开发周期较长,往往需要不断地迭代和更新,因此优化也需要不断地持续和优化。

        学习安卓性能优化的知识和技巧,是每个安卓开发者必备的技能之一。通过掌握安卓性能优化的基本原理和方法,我们可以更加深入地了解安卓设备的工作机制,理解应用的性能瓶颈,从而采取有效的优化策略和措施,提高应用的性能和稳定性,提升用户的满意度和留存率。


       本次介绍安卓性能优化的基本原理、优化策略和实践技巧,帮助开发者更好地了解安卓设备的工作原理,掌握安卓性能优化的基本方法和技巧,从而提高应用的性能和稳定性,为用户提供更加丝滑的使用体验。

安卓的性能优化问题非常广泛,以下是其中一些常见的问题:

  1. 内存泄漏:当应用程序不正确地管理内存时,会发生内存泄漏,导致内存占用过高,甚至导致应用程序崩溃。

  2. 布局优化:布局是应用程序中最常见的性能瓶颈之一,因为过于复杂的布局会导致应用程序响应缓慢或卡顿。

  3. 图片优化:图片是应用程序中占用内存最多的资源之一,因此必须谨慎使用,并对其进行适当的压缩和缓存,以确保应用程序的性能。

  4. 网络请求优化:网络请求可以在应用程序中占用大量的时间和资源,因此必须对其进行优化,以减少请求次数和提高响应速度。

  5. 数据库优化:当应用程序需要大量访问数据库时,可能会导致性能问题。通过优化数据库设计和使用适当的数据库缓存,可以提高应用程序的性能。

  6. 多线程优化:多线程可以提高应用程序的性能,但如果不正确地使用它们,则可能导致死锁、线程竞争和其他问题。

  7. 内存优化:内存是应用程序性能的重要因素之一。通过及时释放不再需要的内存和避免不必要的内存分配,可以提高应用程序的性能。

  8. 代码优化:优化代码结构和算法可以提高应用程序的性能。例如,使用更快速和有效的数据结构和算法来提高应用程序的响应速度。

  9. 安全性优化:安全问题也可能对应用程序的性能产生负面影响。通过避免不安全的代码实践和使用加密技术来保护数据,可以提高应用程序的安全性和性能。

Android的性能优化归结到底就是内存问题,而内存层次的优化,不仅是描述中的这些常规优化项,还可以进行磁盘读写次数、磁盘页数据同步等进一步的优化。

一.内存泄漏

内存泄漏是指应用程序在运行过程中,无法正确地释放已经不再使用的内存资源,导致内存占用不断增加,最终导致应用程序崩溃或运行缓慢。

内存泄漏的原理

安卓内存泄漏的原理是指应用程序在使用内存时,由于程序设计问题或者错误,导致无法释放不再使用的内存,最终导致系统中的内存不足,影响系统的稳定性和性能。


以下是一些可能导致安卓内存泄漏的常见原因:

对象引用未释放

当对象被创建时,如果没有被正确释放,那么这些对象就会一直占用内存,直到应用程序退出。例如,当一个Activity被销毁时,如果它还持有其他对象的引用,那么这些对象就无法被垃圾回收器回收,从而导致内存泄漏。

如果存在内存泄漏,那么这些内存中的对象就会被引用,无法被垃圾回收机制回收,这时我们需要通过GCRoot来识别内存泄漏的对象和引用。

GCRoot是垃圾回收机制中的根节点,根节点包括虚拟机栈、本地方法栈、方法区中的类静态属性引用、活动线程等,这些对象被垃圾回收机制视为“活着的对象”,不会被回收。


当垃圾回收机制执行时,它会从GCRoot出发,遍历所有的对象引用,并标记所有活着的对象,未被标记的对象即为垃圾对象,将会被回收。


当存在内存泄漏时,垃圾回收机制无法回收一些已经不再使用的对象,这些对象仍然被引用,形成了一些GCRoot到内存泄漏对象的引用链,这些对象将无法被回收,导致内存泄漏。


通过查找内存泄漏对象和GCRoot之间的引用链,可以定位到内存泄漏的根源,进而解决内存泄漏问题,LeakCancry就是通过这个机制实现的。一些常见的GCRoot包括:

  • 虚拟机栈(Local Variable)中引用的对象。

  • 方法区中静态属性(Static Variable)引用的对象。

  • JNI 引用的对象。

  • Java 线程(Thread)引用的对象。

  • Java 中的 synchronized 锁持有的对象。

匿名内部类造成的内存泄漏

匿名内部类通常会持有外部类的引用,如果外部类的生命周期比匿名内部类长,(更正一下,这里用生命周期不太恰当,当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量,它们只是在外部类的作用域内创建的对象,所以内部类的销毁时机和外部类的销毁时机是不同的,所以会不会取决与对应对象是否存在被持有的引用)那么就会导致外部类无法被回收,从而导致内存泄漏。

静态变量持有Activity或Context的引用

如果一个静态变量持有Activity或Context的引用,那么这些Activity或Context就无法被垃圾回收器回收,从而导致内存泄漏。

未关闭的Cursor、Stream或者Bitmap对象

如果程序在使用Cursor、Stream或者Bitmap对象时没有正确关闭这些对象,那么这些对象就会一直占用内存,从而导致内存泄漏。

资源未释放

如果程序在使用系统资源时没有正确释放这些资源,例如未关闭数据库连接、未释放音频资源等,那么这些资源就会一直占用内存,从而导致内存泄漏。

常见的内存泄漏

静态引用导致的内存泄漏

当一个对象被一个静态变量持有时,即使这个对象已经不再使用,也不会被垃圾回收器回收,这就会导致内存泄漏。

public class MySingleton {private static MySingleton instance;private Context context;private MySingleton(Context context) {this.context = context;}public static MySingleton getInstance(Context context) {if (instance == null) {instance = new MySingleton(context);}return instance;}
}

上面的代码中,MySingleton持有了一个Context对象的引用,而MySingleton是一个静态变量,导致即使这个对象已经不再使用,也不会被垃圾回收器回收。

注意事项:如果需要使用静态变量,请注意在不需要时将其设置为null,以便及时释放内存。

匿名内部类导致的内存泄漏

匿名内部类会隐式地持有外部类的引用,如果这个匿名内部类被持有了,就会导致外部类无法被垃圾回收。

public class MyActivity extends Activity {private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);button = new Button(this);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// do something}});setContentView(button);}
}

匿名内部类OnClickListener持有了外部类MyActivity的引用,如果MyActivity被销毁之前,button没有被清除,就会导致MyActivity无法被垃圾回收。(此处可以将Button 看作是自己定义的一个对象,一般解法是将button对象置为空)

注意事项:在Activity销毁时,应该将所有持有Activity引用的对象设置为null。

Handler引起的内存泄漏

Handler是在Android应用程序中常用的一种线程通信机制,如果Handler被错误地使用,就会导致内存泄漏。

public class MyActivity extends Activity {private static final int MSG_WHAT = 1;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_WHAT:// do somethingbreak;default:super.handleMessage(msg);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mHandler.sendEmptyMessageDelayed(MSG_WHAT, 1000 * 60 * 5);}@Overrideprotected void onDestroy() {super.onDestroy();// 在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。mHandler.removeCallbacksAndMessages(null);}
}

Handler持有了Activity的引用,如果Activity被销毁之前,Handler的消息队列中还有未处理的消息,就会导致Activity无法被垃圾回收。


注意事项:在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。

Bitmap对象导致的内存泄漏

当一个Bitmap对象被创建时,它会占用大量内存,如果不及时释放,就会导致内存泄漏。

public class MyActivity extends Activity {
private Bitmap mBitmap;@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 加载一张大图mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_image);
}@Override
protected void onDestroy() {super.onDestroy();// 释放Bitmap对象mBitmap.recycle();mBitmap = null;
}
}

当Activity被销毁时,Bitmap对象mBitmap应该被及时释放,否则就会导致内存泄漏。

注意事项:当使用大量Bitmap对象时,应该及时回收不再使用的对象,避免内存泄漏。另外,可以考虑使用图片加载库来管理Bitmap对象,例如Glide、Picasso等。

资源未关闭导致的内存泄漏

当使用一些系统资源时,例如文件、数据库等,如果不及时关闭,就可能导致内存泄漏。例如:

public void readFile(String filePath) throws IOException {FileInputStream fis = null;try {fis = new FileInputStream(filePath);// 读取文件...} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}
}

上面的代码中,如果在读取文件之后没有及时关闭FileInputStream对象,就可能导致内存泄漏。


注意事项:在使用一些系统资源时,例如文件、数据库等,要及时关闭相关对象,避免内存泄漏。


避免内存泄漏需要在编写代码时时刻注意,及时清理不再使用的对象,确保内存资源得到及时释放。同时,可以使用一些工具来检测内存泄漏问题,例如Android Profiler、LeakCanary等。

WebView 内存泄漏

当使用WebView时,如果不及时释放,就可能导致内存泄漏。

public class MyActivity extends Activity {private WebView mWebView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = findViewById(R.id.webview);mWebView.loadUrl("https://www.example.com");}@Overrideprotected void onDestroy() {super.onDestroy();// 释放WebView对象if (mWebView != null) {mWebView.stopLoading();mWebView.clearHistory();mWebView.clearCache(true);mWebView.loadUrl("about:blank");mWebView.onPause();mWebView.removeAllViews();mWebView.destroy();mWebView = null;}}
}

上面的代码中,当Activity销毁时,WebView对象应该被及时释放,否则就可能导致内存泄漏。

注意事项:在使用WebView时,要及时释放WebView对象,可以在Activity销毁时调用WebView的destroy方法,同时也要清除WebView的历史记录、缓存等内容,以确保释放所有资源。

监测工具

  1. 内存监视工具:Android Studio提供了内存监视工具,可以在开发过程中实时监视应用程序的内存使用情况,帮助开发者及时发现内存泄漏问题。

  2. DDMS:Android SDK中的DDMS工具可以监视Android设备或模拟器的进程和线程,包括内存使用情况、堆栈跟踪等信息,可以用来诊断内存泄漏问题。

  3. MAT:MAT(Memory Analyzer Tool)是一款基于Eclipse的内存分析工具,可以分析应用程序的堆内存使用情况,识别和定位内存泄漏问题。

  4. 腾讯的Matrix,也是非常好的一个开源项目,推荐大家使用。

二.总结

内存泄漏是指程序中的某些对象或资源没有被妥善地释放,从而导致内存占用不断增加,最终可能导致应用程序崩溃或系统运行缓慢等问题。


常见的内存泄漏问题包括:

  1. 长时间持有Activity或Fragment对象导致的内存泄漏;

  2. 匿名内部类和非静态内部类导致的内存泄漏;

  3. WebView持有Activity对象导致的内存泄漏;

  4. 单例模式持有资源对象导致的内存泄漏;

  5. 资源未关闭导致的内存泄漏;

  6. 静态变量持有Context对象导致的内存泄漏;

  7. Handler持有外部类引用导致的内存泄漏;

  8. Bitmap占用大量内存导致的内存泄漏;

  9. 单例持有大量数据导致的内存泄漏。

为避免内存泄漏问题,我们可以采取以下措施:

  1. 及时释放Activity或Fragment对象;

  2. 避免匿名内部类和非静态内部类;

  3. 在使用WebView时,及时调用destroy方法;

  4. 在单例模式中避免长时间持有资源对象;

  5. 及时关闭资源对象;

  6. 避免静态变量持有Context对象;

  7. 避免Handler持有外部类引用;

  8. 在使用Bitmap时,及时释放内存;

  9. 避免单例持有大量数据。

以上为Android性能优化的总结,内存泄漏的场景不同,优化的方法也不唯一,欢迎大家一起讨论。

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

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

相关文章

vue3、react组件数据传值对比分析——父组件传递子组件,子组件传递父组件

文章目录 ⭐前言⭐react 组件传值实例💖父组件传值给子组件(props)💖子组件传递事件给父组件props绑定事件💖父组件触发子组件的事件Ref ⭐vue3 组件传值实例💖 父组件传递数据给子组件props💖 …

【论文阅读】SHADEWATCHER:使用系统审计记录的推荐引导网络威胁分析(SP-2022)

SHADEWATCHER: Recommendation-guided CyberThreat Analysis using System Audit Records S&P-2022 新加坡国立大学、中国科学技术大学 Zengy J, Wang X, Liu J, et al. Shadewatcher: Recommendation-guided cyber threat analysis using system audit records[C]//2022 I…

学C的第三十四天【程序环境和预处理】

相关代码gitee自取: C语言学习日记: 加油努力 (gitee.com) 接上期: 学C的第三十三天【C语言文件操作】_高高的胖子的博客-CSDN博客 1 . 程序的翻译环境和执行环境 在ANSI C(C语言标准)的任何一种实现中,存在两个不同的环境。 &#xff0…

容器虚拟化基础之cgroups/LXC

"你真的,自由了~" 容器虚拟化基础之Cgroups: (1) 什么是cgroups cgroups是 linux 内核提供的一种机制, 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一…

3D与沉浸式技术,如何助力企业数字化转型?

说起3D,估计许多读者朋友会在第一时间想起《阿凡达》系列和《侏罗纪公园》系列电影大作。每一帧细节纤毫毕现的逼真画面,让观众几乎分不清虚拟与现实,完全沉浸在导演打造的视觉盛宴中。 事实上,除了大家所熟知的3D影视动画之外&am…

快速学习GO语言总结

备注:本博客将自己初步学习GO的总结进行分享,希望大家通过本博客可以在短时间内快速掌握GO的基本程序编码能力,如有错误请留言指正,谢谢! 一、初步了解Go语言 (一)Go语言诞生的主要问题和目标…

iOS17 widget Content margin

iOS17小组件有4个新的地方可以放置分别是:Mac桌面、iPad锁屏界面、 iPhone Standby模式、watch的smart stack Transition to content margins iOS17中苹果为widget新增了Content margin, 使widget的内容能够距离边缘有一定的间隙,确保内容显示完整。这…

学习设计模式之适配器模式,但是宝可梦

前言 作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。 适配器模式 意图:将一个类的接口转换成客户希望的另一个接口 主要解决:把现有对象放到新环境里,而新…

IDEA 如何制作代码补丁?IDEA 生成 patch 和使用 patch

什么是升级补丁? 比如你本地修复的 bug,需要把增量文件发给客户,很多场景下大家都需要手工整理修改的文件,并整理好目录,这个很麻烦。那有没有简单的技巧呢?看看 IDEA 生成 patch 和使用 patch 的使用。 介…

一、Kafka概述

目录 1.3 Kafka的基础架构 1.3 Kafka的基础架构 Producer:消息生产者,就是向 Kafka broker 发消息的客户端Consumer:消息消费者,向 Kafka broker 取消息的客户端。Consumer Group(CG):消费者组&…

PyTorch学习笔记(十六)——利用GPU训练

一、方式一 网络模型、损失函数、数据(包括输入、标注) 找到以上三种变量,调用它们的.cuda(),再返回即可 if torch.cuda.is_available():mynn mynn.cuda() if torch.cuda.is_available():loss_function loss_function.cuda(…

SpringMVC之@RequestMapping注解

文章目录 前言一、RequestMapping介绍二、详解(末尾附源码,自行测试)1.RequestMapping注解的位置2.RequestMapping注解的value属性3.RequestMapping注解的method属性4.RequestMapping注解的params属性(了解)5.RequestM…

华为ENSP网络设备配置实战6(简单的链路聚合)

题目要求 1、创建聚合组,添加端口成员 2、PC1网段为vlan10,PC2网段为vlan20 3、LSW1为核心网关设备,正确配置PC网关 4、PC1与PC2互通 解题过程 1.1、 按照拓扑图,各个设备起名 sys (进入系统视图) sy…

TCP协议报文结构

TCP是什么 TCP(传输控制协议)是一种面向连接的、可靠的、全双工的传输协议。它使用头部(Header)和数据(Data)来组织数据包,确保数据的可靠传输和按序传递。 TCP协议报文结构 下面详细阐述TCP…

FRP内网穿透,配置本地电脑作为服务器

FRP内网穿透,配置本地电脑作为服务器 下载FRP服务端客户端 参考链接: https://www.it235.com/实用工具/内网穿透/pierce.html https://www.cnblogs.com/007sx/p/17469301.html 由于没有公网ip,所以尝试内网穿透将本地电脑作为服务器&#xff…

【Mariadb高可用MHA】

目录 一、概述 1.概念 2.组成 3.特点 4.工作原理 二、案例介绍 1.192.168.42.3 2.192.168.42.4 3.192.168.42.5 4.192.168.42.6 三、实际构建MHA 1.ssh免密登录 1.1 所有节点配置hosts 1.2 192.168.42.3 1.3 192.168.42.4 1.4 192.168.42.5 1.5 192.168.42.6 …

(二)结构型模式:7、享元模式(Flyweight Pattern)(C++实例)

目录 1、享元模式(Flyweight Pattern)含义 2、享元模式的UML图学习 3、享元模式的应用场景 4、享元模式的优缺点 5、C实现享元模式的简单实例 1、享元模式(Flyweight Pattern)含义 享元模式(Flyweight&#xff09…

OpenCV笔记之solvePnP函数和calibrateCamera函数对比

OpenCV笔记之solvePnP函数和calibrateCamera函数对比 文章目录 OpenCV笔记之solvePnP函数和calibrateCamera函数对比1.cv::solvePnP2.cv::solvePnP函数的用途和工作原理3.cv::solvePnP背后的数学方程式4.cv::SOLVEPNP_ITERATIVE、cv::SOLVEPNP_EPNP、cv::SOLVEPNP_P3P5.一个固定…

为什么需要单元测试?

为什么需要单元测试? 从产品角度而言,常规的功能测试、系统测试都是站在产品局部或全局功能进行测试,能够很好地与用户的需要相结合,但是缺乏了对产品研发细节(特别是代码细节的理解)。 从测试人员角度而言…

Qt应用开发(基础篇)——纯文本编辑窗口 QPlainTextEdit

一、前言 QPlainTextEdit类继承于QAbstractScrollArea,QAbstractScrollArea继承于QFrame,是Qt用来显示和编辑纯文本的窗口。 滚屏区域基类https://blog.csdn.net/u014491932/article/details/132245486?spm1001.2014.3001.5501框架类QFramehttps://blo…