android内存泄漏原因分析,Android Studio3.6的内存泄漏检测功能 VS LeakCanary

e70d66577b65a1ee581d5b105f247b9d.png

2020年2月,谷歌发布了Android Studio 3.6版。它包括一个新的“内存泄漏检测”功能。这是否意味着我们不再需要流行的内存泄漏检测库“Leak Canary”了?在过去的几天里,我花了一些时间来研究android studio的新特性,希望在这里分享我的发现和想法。

内存泄露示例程序

我创建了一个示例应用程序,其中包含一个名为LeakingActivity的活动。顾名思义,此活动演示了导致泄漏的常见原因。它将侦听器定义为内部类,并将该侦听器注册到具有较长生命周期的对象。在我们的例子中,这个对象是一个单例,它的寿命与应用程序的寿命一样长。由于我们在离开活动时没有注销侦听器,所以即使在Android框架调用onDestroy()方法之后,Singleton仍然保留对它的引用。侦听器是活动的内部类,因此对活动有一个隐式引用,因此不会对其进行垃圾收集,也不会从中释放内存空间。这最多会导致不必要的高内存使用率,并导致应用程序崩溃,因为

class LeakingActivity: AppCompatActivity() {

private val listener = Listener()

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_leaking)

}

override fun onStart() {

super.onStart()

GlobalSingleton.register(listener)

}

override fun onStop() {

super.onStop()

// we forget to unregister our listener so a reference

// of the Singleton to the listener and to the Activity

// still exists after the user navigates away fro that

// activity

// GlobalSingleton.unregister(listener)

}

// inner class has implicit reference to enclosing Activity

private inner class Listener : GlobalSingletonListener {

override fun onEvent() { }

}

}

object GlobalSingleton {

// a reference to the listener of the Activity is kept as long as

// unregister isn't called

private val listeners = mutableListOf()

fun register(listener: GlobalSingletonListener) {

listeners.add(listener)

}

fun unregister(listener: GlobalSingletonListener) {

listeners.remove(listener)

}

}

interface GlobalSingletonListener {

fun onEvent()

}

使用Android Studio 3.6查找内存泄漏

为了使用新的“泄漏检测”功能检查应用程序是否存在泄漏,您必须启动Android Studio内存探查器。如果您是在Android 7.1或更低版本的设备上运行应用程序,则必须启用高级分析才能查看所有分析数据。单击导航栏中的“配置文件应用程序”,安装、启动并配置您的应用程序。

5f02a077b883d7275bfd54bbe1eb01d7.png

要开始分析已经运行的应用程序,只需单击Android Studio底部栏中的“Profiler”,然后单击(+)添加会话:

873f8697ee18fc2fb3c6313754b41fc5.png

73dd5c4ce8b1ee48c7b5287bce41d629.png

我切换到我的模拟器,打开和关闭泄漏活动两次。在探查器中,我们看到LeakingActivity被创建,然后被销毁。

0ae958c4918db7552ba3d6ae57c0445a.png

现在我们想知道这个活动是否被垃圾收集。因此,我们必须通过单击内存通道来打开内存探查器,这在我看来有点不直观。我们可以看到为不同类别的对象(Java、Native、Graphics…)分配的所有内存。

50f0a520788d9d9287a2db24cdf66734.png

接下来,我们必须通过单击顶部的图标来转储Java堆。

c3029691d1888cba09c5ddc7e8378fa6.png

通过单击force garbage collection first强制垃圾收集是有意义的,只是为了确保垃圾收集确实发生了。

8dc5331ac32d972ff4b9d2d28e81f319.png

转储完成后,我们可以看到所有内存分配:

faef11738ebc24e044655462e681f693.png

Android Studio 3.6中的新功能。是以下复选框:

cc92b0da50d0b7cad884180b226dff55.png

通过选中此新复选框,我们可以看到我们的LeakingActivity泄漏了两次:

68b0a259b7e13584b1f1c63b261bc393.png

调查内存泄密的原因

下一步我们显然要做的是修复漏洞。当对不再使用的“死”对象的引用仍然存在时,就会发生内存泄漏。我们需要找到这些参考资料并把它们处理掉。让我们通过单击LeakingActivity打开右侧面板上的实例视图来进行一些调查。

f9eec83b9e14c5ffcbc5b3d0af5e9fd5.png

通过单击实例前面的箭头,我们可以在“引用视图”中看到对该实例的所有引用:

e5c4525d0f76f32b8408d29027ffa505.png

老实说,我不知道找到导致泄漏的确切参考路径的最佳方法是什么。我做了一些研究来找出LeakCanary是如何得到导致泄漏的参考路径的。在LeakCanary中,此路径称为“Leak Trace”,根据其文档,它是“从垃圾收集根到保留对象的最佳强引用路径”。在其他地方,我看到它是最短路径。垃圾收集根是永远不会被垃圾收集的对象(比如应用程序对象,或者在我们的例子中是单例对象)。

保留的对象是LeakingActivity。我不确定是否可以在实例视图中看到引用的类型(强、弱等)。但是,我们可以根据深度来排序引用,这是根据内存探查器文档“从任何垃圾收集根到所选实例的最短跃点数”。

826ca27c5b626ce3dddede7c9ffd64e5.png

当我们查看最短深度为3的引用的第一条路径时,我们可以确定以下引用链:GlobalSingleton(垃圾收集根)有一个对listener ArrayList的引用,它有一个对活动的listener内部类的引用,该类有一个对LeakingActivity的(隐式)引用。

df24d83b7cb159fb9847652994083245.png

这看起来很可疑,因为GlobalSingleton不应该引用已销毁的活动,然后我们意识到忘记注销侦听器。

class LeakingActivity: AppCompatActivity() {

private val listener = Listener()

...

override fun onStop() {

super.onStop()

// fixing the leak by unregistering the listener

GlobalSingleton.unregister(listener)

}

...

}

我们发现并修复了新的泄漏通过Android Studio检测功能!所以我们不再需要金丝雀了,对吧?

嗯,使用LeakCanary来检测漏洞还是有很多好处的。

8cd49bf63ce13bf4ec882ebbc3b1c24c.png

首先,LeakCanary总是监视你的应用程序是否存在漏洞。使用Android Studio Profiler,你必须主动监控你的应用程序,我们都知道,作为开发人员,我们通常有很多其他事情要做,而且倾向于“稍后”进行分析(也就是从不)。LeakCanary不断地“提醒”你还有一些漏洞需要修复,这增加了你实际修复它们的机会。

7ac28dd8eed6271fdf913f08aaa003ad.png

第二,使用LeakCanary更容易找到泄漏的原因。我们不必深究android studio令人困惑的“参考视图”,但可以得到一个很好的“泄漏跟踪”。它甚至在最有可能导致泄漏的参考点下方画了一条红色的曲线。

c5d03766d3f6013cb5ead8215d7548d4.png

第三,LeakCanary收集了库和Android框架中已知的漏洞,因此它不会向您显示无法修复的漏洞。此外,通过将LeakCanary添加到objectWatcher,您不仅可以检测活动或片段的泄漏,还可以检测任何其他对象(例如服务或Dagger组件)的泄漏。

此外,其他特性,如在生产中计数保留对象或在运行仪表测试时进行泄漏检测,也非常有用。

结论

android studio3.6新的“泄漏检测”功能是一种很好的、方便的方法,可以检测泄漏的片段和活动,而无需向应用程序中添加第三方库。在我看来,偶尔检查一下小型或业余爱好的应用程序是否有漏洞就足够了。不过,找出内存泄漏的原因并不是那么容易。对于任何关心低内存占用率和

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值