预防和检测内存泄漏:实用技巧

简介: 内存泄漏(Memory leak)是指程序在运行过程中分配的内存资源没有被正确释放,导致这部分内存无法再被程序使用,最终消耗了系统的可用内存。

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

@TOC

在这里插入图片描述
ddd

一、导读

我们继续总结学习Android 基础知识,温故知新。

二、概览

内存泄漏(Memory leak)是指程序在运行过程中分配的内存资源没有被正确释放,导致这部分内存无法再被程序使用,最终消耗了系统的可用内存。

内存泄漏可能会导致以下问题:

  1. 内存耗尽:内存泄漏会导致程序占用的内存越来越多,最终导致系统可用内存耗尽,使程序无法继续运行。
  2. 性能下降:由于内存泄漏的存在,系统中的可用内存减少,导致系统的性能下降,运行速度变慢。
  3. 程序崩溃:当内存泄漏严重时,系统无法分配到足够的内存来执行程序,导致程序崩溃或者无法启动。

内存泄漏的原因可能包括:

  1. 错误的内存分配和释放:程序中可能存在分配内存后未正确释放的情况,导致内存泄漏。
  2. 对象引用未释放:当一个对象的引用无法被正常释放时,其中包含的内存也无法被回收,导致内存泄漏。
  3. 循环引用:两个或多个对象之间互相引用,且没有被其他引用指向时,可能导致内存泄漏。
  4. 缓存未清理:缓存或临时存储的数据长时间保留在内存中,没有及时清理,可能导致内存泄漏。

在开发过程中,可以使用内存分析工具来检测和解决内存泄漏问题,如内存检测工具、代码审查等。

三、案例分析

不同的工具有不同的使用场景,对应线下场景,我们先用 android studio自带的工具,
我们捕获堆转储文件分析

3.1 使用 memory-profiler

//匿名内部类
private Handler mHandler = new Handler(){
   
   
    @Override
    public void handleMessage(@NonNull Message msg){
   
   
        super.handleMessage(msg);
    }
}

我们先说下原因,匿名内部类默认持有外部类的引用,如果这个mHandler有个延时任务未执行,此时退出activity页面,
此时Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏。

在这里插入图片描述

我们大致看下引用链,也能看到泄漏的地方
在这里插入图片描述

3.2 使用MAT(Memory Analyzer)

将上面的堆转储另存为 HPROF 文件,再用mat打开

hprof-conv heap-original.hprof heap-converted.hprof

打开后预览页面是这样的,
在这里插入图片描述

3.2.1 通过OQL语法

我们直接通过OQL语法来排查内存泄漏,如上图箭头所示的地方,我们在oql页面输入下面的查询代码,然后按下图中红色的小箭头,

select * from instanceof android.app.Activity

会在下面出现查询结果,如下图所示,可以看到,查询到了9个一样的 MemoryShakeActivity,这肯定是有问题的 ,

那我们如何确认是哪里有问题呢?
我们在其中一个activity上右键点击,然后会出现一个菜单,选择 path to gc root, 再选择最长的那个选项 exclude all ...
在这里插入图片描述

最终,我们在mat里面会出现一个新的页面,也就找到了内存泄漏的地方,看下图:
在这里插入图片描述

3.2.2 通过 Histogram(直方图)

它可以列出任意一个类的实例数(每个对象的统计)。它支持使用正则表达式来查找某个特定的类,还可以计算出该类所有对象的保留堆最小值或者精确值,

我们可以通过正则表达式输入 MemoryShakeActivity, 看到Histogram列出了与 MemoryShakeActivity 相关的类

然后选中一行,然后依次选择,点击右键 -> Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc.references(排查虚引用/弱引用/软引用等)
在这里插入图片描述

然后我们就可以看到哪里泄漏了

在这里插入图片描述

Merge Shortest Paths to GC Roots 可以查看一个对象到RC Roots是否存在引用链相连接,
在JAVA中是通过可达性(Reachability Analysis)来判断对象是否存活,这个算法的基本思想是通过一系列的称谓"GC Roots"的对象作为起始点,
从这些节点开始向下搜索,搜索所走得路径称为引用链,当一个对象到GC Roots没有任何引用链相连则该对象被判定为可以被回收的对象,反之不能被回收,

虚引用/弱引用/软引用的对象可以直接被GC给回收.

3.2.3 通过 Dominator Tree(支配树)

Dominator Tree(支配树)提供了程序中最占内存的对象的排列。

使用方法跟Histogram(直方图)差不多,按照上面类似的操作即可。

3.2.4 通过 leak suspects (泄露疑点)

如下图
在这里插入图片描述

四、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

相关文章
|
2月前
|
监控 Java 数据库连接
解析与预防:Java中的内存泄漏问题
解析与预防:Java中的内存泄漏问题
|
3月前
|
缓存 监控 Python
在Python中,如何检测和处理内存泄漏?
【2月更文挑战第7天】【2月更文挑战第18篇】在Python中,如何检测和处理内存泄漏?
|
4月前
|
安全 Linux 编译器
内存泄漏检测组件的分析与实现(linux c)-mtrace工具使用
内存泄漏产生原因 在堆上使用malloc/remalloc/calloc分配了内存空间,但是没有使用free释放对应的空间。
76 0
|
4月前
|
Java C++
动手实现内存泄漏检测组件
动手实现内存泄漏检测组件
37 1
|
4月前
使用mtrace进行内存泄漏检测
使用mtrace进行内存泄漏检测
76 1
|
4月前
|
调度 C语言
内存泄漏检测相关内容
内存泄漏检测相关内容
25 0
|
2月前
|
IDE Linux 开发工具
内存泄漏检测工具Valgrind:C++代码问题检测的利器(一)
内存泄漏检测工具Valgrind:C++代码问题检测的利器
95 0
|
1天前
|
Dart 前端开发 Java
【Flutter前端技术开发专栏】Flutter中的内存泄漏检测与解决
【4月更文挑战第30天】本文探讨了Flutter应用中的内存泄漏检测与解决方法。内存泄漏影响性能和用户体验,常见原因包括全局变量、不恰当的闭包使用等。开发者可借助`observatory`工具或`dart_inspector`插件监测内存使用。解决内存泄漏的策略包括避免长期持有的全局变量、正确管理闭包、及时清理资源、妥善处理Stream和RxDart订阅、正确 disposal 动画和控制器,以及管理原生插件资源。通过这些方法,开发者能有效防止内存泄漏,优化应用性能。
【Flutter前端技术开发专栏】Flutter中的内存泄漏检测与解决
|
1天前
|
开发工具 Swift iOS开发
【Swift开发专栏】Swift中的内存泄漏检测与修复
【4月更文挑战第30天】本文探讨了Swift中的内存泄漏问题,尽管有ARC机制,但仍需关注内存管理。文章分为三部分:内存管理基础知识、检测方法和修复技巧。了解ARC原理和循环引用陷阱是防止内存泄漏的关键。检测方法包括使用Xcode内存调试器、LeakSanitizer和性能分析工具。修复技巧涉及打破循环引用、使用弱/无主引用及手动管理内存。理解这些对优化应用性能和稳定性至关重要。
|
2月前
|
缓存 Linux iOS开发
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
68 1