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,一经查实,立即删除!

相关文章

【SA8295P 源码分析】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析

【SA8295P 源码分析】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析 一、emac_rx_thread_handler():通过POLL 轮询方式获取数据二、emac_rx_poll_mq():调用 pdata->clean_rx() 来处理消息三、emac_configure_rx_fun_ptr():配置 pdata->…

SWUST 派森练习题:P116. 字符统计排序

描述 编写程序:从键盘中输入一段字符,完成以下统计并输出: (1)该段字符中总共出现了多少种不同类型的字符; (2)出现次数最多的前3个字符(只能为字母和数字&#xff0c…

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…

Hlang-中英双语言编程语言使用手册

文章目录 介绍Hlang基本使用下载配置环境变量特性中文关键字支持中文符号混合编程中文错误提示终端多行输入基本数据类型整数浮点数列表字符串基本操作变量定义逻辑判断基本运算条件判断循环函数介绍 Hlang是一款基于Python编写的支持中英文混合编程的动态语言。其简单易上手,…

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

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

for循环也能写错?

好开心好开心 可是明明也只是解决了一个不应该要犯错的地方 本来是要在一个大循环中,每一次循环中有30次的另一个循环,然而我写成了这个样子: consensus_iterations 30 for i in range(2):for consensus_iterations in range(consensus_it…

容器虚拟化基础之cgroups/LXC

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

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

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

查找算法及应用

查找算法及应用 常用查找算法包括顺序/线性查找、二分/折半查找、插值查找、斐波那契查找。 线性查找 逐一比对,发现有相同值返回即可。 二分查找 前提是数组有序。如果数据是连续的情况下可以使用插值查找。 插值查找 插值查找算法类似二分查找,不同的…

快速学习GO语言总结

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

8.3 【C语言】通过指针引用数组

8.3.1 数组元素的指针 所谓数组元素的指针就是数组元素的地址。 可以用一个指针变量指向一个数组元素。例如: int a[10]{1,3,5,7,9,11,13,15,17,19}; int *p; p&a[0]; 引用数组元素可以用下标法,也可以用指针法&#xf…

iOS17 widget Content margin

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

【WinAPI详解】<CreateWindowEx详解>

函数原型: HWND CreateWindowEx(DWORD dwExStyle, //窗口的扩展风格(加强版专有)LPCTSTR lpClassName, //已经注册的窗口类名称LPCTSTR lpWindowName,//窗口标题栏的名字DWORD dwStyle, //窗口的基本风格int x, //窗口左上角水平坐标位置int …

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

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

通过C实现sqlite3操作,(增删改查),导入电子词典

一、插入 #include <stdio.h> #include <sqlite3.h> void do_insert(sqlite3 *db); int main(int argc, const char *argv[]) {//创建并打开一个数据库sqlite3 *db NULL;if(sqlite3_open("./mysql.db",&db) ! SQLITE_OK){fprintf(stderr,"sql…

AUTOSAR配置与实践(配置篇)5.1 BSW的通信功能进阶

传送门 -> AUTOSAR配置与实践总目录 AUTOSAR配置与实践(配置篇)5.1 BSW的通信功能进阶 一、模块和收发流程主要配置项介绍1.1 模块介绍1.2 通知方式相关主要配置项二、收发流程详解2.1 发送流程2.2 发送确认过程2.2.1 发送确认过程(读数据方式:轮询)2.2.2 发送确认过程…

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

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

Centos升级openssl

依赖包 安装编译 OpenSSL 所需的包&#xff0c;包括 gcc、make、perl 和 zlib-devel。可以通过运行以下命令完成&#xff1a; yum install -y gcc make perl zlib-devel安装包下载 下载 OpenSSL 1.1.1 的源码包&#xff0c;可以从 OpenSSL 官网下载&#xff08;https://www.op…

一、Kafka概述

目录 1.3 Kafka的基础架构 1.3 Kafka的基础架构 Producer&#xff1a;消息生产者&#xff0c;就是向 Kafka broker 发消息的客户端Consumer&#xff1a;消息消费者&#xff0c;向 Kafka broker 取消息的客户端。Consumer Group&#xff08;CG&#xff09;&#xff1a;消费者组&…