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

简介: 内存泄漏(Memory leak)是指程序在运行过程中分配的内存资源没有被正确释放,导致这部分内存无法再被程序使用,最终消耗了系统的可用内存。
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

在这里插入图片描述
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学习专栏]

相关文章
|
5月前
|
Java 数据库连接
Java中的内存泄漏排查与预防方法
Java中的内存泄漏排查与预防方法
|
20天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
40 6
|
29天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
158 9
|
29天前
|
监控 JavaScript 前端开发
如何检测和解决 JavaScript 中内存泄漏问题
【10月更文挑战第25天】解决内存泄漏问题需要对代码有深入的理解和细致的排查。同时,不断优化和改进代码的结构和逻辑也是预防内存泄漏的重要措施。
41 6
|
1月前
|
Web App开发 缓存 JavaScript
如何检测和解决闭包引起的内存泄露
闭包引起的内存泄露是JavaScript开发中常见的问题。本文介绍了闭包导致内存泄露的原因,以及如何通过工具检测和代码优化来解决这些问题。
|
5月前
|
存储 算法 Java
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
Java面试题:深入探究Java内存模型与垃圾回收机制,解释JVM中堆内存和栈内存的主要区别,谈谈对Java垃圾回收机制的理解,Java中的内存泄漏及其产生原因,如何检测和解决内存泄漏问题
66 0
|
3月前
|
C语言 Android开发 C++
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
本文介绍了使用MTuner软件进行Qt MinGW编译程序的内存泄漏检测的方法,提供了MTuner的下载链接和测试代码示例,并通过将Debug程序拖入MTuner来定位内存泄漏问题。
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
|
2月前
|
Web App开发 开发者
|
2月前
|
缓存 监控 Java
内存泄漏:深入理解、检测与解决
【10月更文挑战第19天】内存泄漏:深入理解、检测与解决
58 0
|
2月前
|
设计模式 Java Android开发
安卓应用开发中的内存泄漏检测与修复
【9月更文挑战第30天】在安卓应用开发过程中,内存泄漏是一个常见而又棘手的问题。它不仅会导致应用运行缓慢,还可能引发应用崩溃,严重影响用户体验。本文将深入探讨如何检测和修复内存泄漏,以提升应用性能和稳定性。我们将通过一个具体的代码示例,展示如何使用Android Studio的Memory Profiler工具来定位内存泄漏,并介绍几种常见的内存泄漏场景及其解决方案。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的技巧和方法,帮助你打造更优质的安卓应用。