内存泄漏分析

一、什么是内存泄漏?

内存泄漏是指在程序运行过程中,分配给程序的内存空间在不需要时没有被正确释放或回收的情况。

二、Java层内存泄漏。

  • Android应用程序中Java层常见的内存泄漏情况:
  1. Context引用泄漏:Android中的Context是一个常见的泄漏源。例如,在生命周期长于Context的类中持有Context引用,导致Context无法被垃圾回收。
  2. 非静态内部类和匿名内部类:非静态内部类和匿名内部类会隐式持有外部类的引用,如果它们的生命周期比外部类更长,就会导致外部类无法被垃圾回收。
  3. Handler引起的泄漏:使用Handler时,如果Handler持有Activity或Fragment的引用,并在消息队列中排队等待处理,那么这些Activity或Fragment无法被垃圾回收。
  4. 单例模式:不正确的使用单例模式可能导致对象长时间存活在内存中,造成内存泄漏。确保单例对象在不需要时能够释放相关引用。
  5. 监听器和回调:如果注册的监听器和回调没有及时取消注册,这些监听器和回调持有的引用可能阻止相关对象的垃圾回收。
  6. 静态引用导致的泄漏:使用静态引用可能导致对象在应用程序的整个生命周期内持续存在,即使不需要也不会释放,从而导致内存泄漏。
  7. 长时间的后台任务:在Android应用中进行长时间的后台任务,如果不正确的处理,可能会导致相关资源无法释放,从而引发内存泄漏。
  8. 未关闭的资源:Bitmap资源、文件句柄、数据库连接、网络连接等未关闭也可能导致内存泄漏。

三、Native层内存泄漏。

  • Android应用程序中Native层常见的内存泄漏情况:
  1. 内存分配未释放:在C/C++代码中使用malloc、new等函数分配内存,但未使用free、delete等函数释放,导致内存泄漏。
  2. 未释放的本地引用:在JNI中,如果分配了本地引用(Local Reference)但未正确释放,会导致内存泄漏。
  3. 未释放的全局引用:在JNI中,如果分配了全局引用(Global Reference)但未正确释放,会导致内存泄漏。
  4. 资源未释放:打开文件、网络连接、数据库连接等资源,但在不在需要时未正确关闭,导致资源泄漏。
  5. 未释放的线程和锁:在本地代码中创建了线程、锁等资源,但在不在需要时未正确释放,可能导致线程或锁资源泄漏。
  6. 循环引用:在C++等编程中,对象之间可能存在循环引用,使引用计数无法降为零,从而导致内存泄漏。
  7. 异常处理不当:在本地代码中发生异常时,如果未正确释放已分配的内存和资源,会导致内存无法释放。

四、内存泄漏如何检测。

进行内存泄漏检测建议使用Android Profiler的内存分析器,它能够显示内存使用的实时图表,并且可以捕获内存快照、强制执行垃圾回收以及动态跟踪内存分配和回收。

  1. Capture heap dump:获取当前内存快照。
  2. Record native allocations:记录一段时间内C/C++内存分配和回收。
  3. Record Java/kolin allocation:记录一段时间内Java/Kotlin对象的分配和回收。
  4. 内存使用详情:显示应用当前的内存使用详情。
  5. 强制执行垃圾回收。
  • Java内存泄漏检测

Java内存泄漏检测分两种场景:

1、严重泄漏,内存增加速度快。

对于严重的内存泄漏,内存泄漏对象的数量会明显增加,快速占据程序内存的大部分空间,对于这种泄漏场景,通过Record Java/kolin allocation功能,记录该时间段内Java对象的分配和回收。其中数量大而且占用内存远超其它对象的对象即为内存泄漏的对象。

标号1显示了对象的分配详情(数量、内存),通过这些数据,我们能够大致判断出来泄漏的对象,然后通过编号2,根据对象创建的堆栈信息(如果没有堆栈信息,则该对象在JNI中创建),定位到具体的对象。

2、轻微泄漏,内存缓慢增加。

对于轻微的内存泄漏,需要经过长时间的运行,让泄漏的内存占据程序的大部分空间,再通过Capture heap dump功能,获取当前的内存快照。

然后通过标号1查看数量大、内存占用大的对象,通过标号2进一步分析该对象引用情况,查找不被释放的原因,最后定位到问题。

  • Native内存泄漏

在C/C++中,内存管理相对更加手动,没有内置的机制来直接捕获内存状态,因此没法实现内存快照功能,但是通过Android Profiler的内存分析器,通过Record native allocations功能,可以动态跟踪C/C++内存分配和回收,从而实现native内存泄漏检测。

在Native内存泄漏检测过程中,不管是严重泄漏,还是轻微泄漏,都需要让泄漏的内存占据整个程序内存的大部分空间,这样方便我们检测到内存泄漏的位置。

标号1应该选择Array by callstack,便于我们查看调用信息,标号2 Module Name,进行分析时,应该选择应用中明确使用的so库,标号3是函数的编码,通过这些信息,可以定位到具体函数,最后找出泄漏的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值