10

我在 Android (Java) 中遇到了一个名为LeakCanary的内存泄漏检测库,但无法理解它们泄漏内存的示例。任何人都可以解释他们示例中显示的代码如何以及为什么是内存泄漏。

class Cat {
}
class Box {
  Cat hiddenCat;
}
class Docker {
  static Box container;
}

// ...

Box box = new Box();
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;

然后他们观察变量schrodingerCat的泄漏,泄漏如下所示(我不知道如何与上面的代码相关联)。

* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance

任何有关泄漏解释以及检测如何与之相关的帮助都将非常有帮助。还有一些适合初学者的好文章会很好。

谢谢!

4

3 回答 3

14

首先,让我们了解什么是内存泄漏

定义

内存泄漏是在 RAM 中分配的数据(位图、对象、数组等),垃圾收集器 (GC) 无法释放,尽管程序不再需要它。

例子

用户正在打开显示图像的视图。我们将位图加载到内存中。现在用户退出视图并且不再需要图像并且代码中没有对它的引用。在那一刻,GC 开始行动并将其从内存中删除。但是,如果我们仍然有对它的引用,GC 将不知道它可以删除,并且它会留在 RAM 中占用不需要的空间 - 也就是Memory Leak

盒子里的猫

假设我们的应用程序中有一个 Cat 对象,我们将它保存在一个 Box 对象中。如果我们持有盒子(有一个 Box 对象的引用)并且盒子持有 Cat,GC 将无法从内存中清除 Cat 对象。

Docker 是一个对我们的 Box 有静态引用的类。这意味着除非我们将其无效或重新分配值,否则 Docker 将继续引用 Box。防止 GC 将 Box(和内部 Cat)从内存中删除。

那么,我们需要猫吗?它仍然与应用程序相关吗?

这取决于开发人员来决定我们需要 Cat 多长时间。 LeakCanary和其他诊断工具提示可能存在内存泄漏。他们认为对象(猫)可能不再需要,所以他们警告它是泄漏。

回顾

在示例中,它们给出了内存泄漏的常见场景。当使用静态引用时,我们会阻止 GC 清理对象。您应该阅读以下内容:

* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance

作为:

  • 对象 Cat可能不再使用,但没有被 GC 从内存中删除。
  • 对象 Cat 没有被移除的原因是因为 Box 引用了它。
  • 没有删除对象 Box 的原因是 Docker 对它有一个静态引用。
  • Docker 的静态引用是导致可能泄漏的树的根。
于 2015-07-15T10:59:57.567 回答
1

它看起来像RefWatcher用于“观察变量schrodingerCat是否泄漏”的实例:

refWatcher.watch(schrodingerCat);

强制进行一组 GC 传递,如果在这些 GC 传递期间未收集传入的引用,则将其视为泄漏。

由于 staticDocker.container.hiddenCat保留了对最初称为 的对象的 GC 根引用,因此当您要求检查它schrodingerCat时,它不能被GC 处理。RefWatcher因此,它让您知道无法收集该对象。

于 2015-05-22T20:55:33.940 回答
1

我建议你阅读这个答案https://stackoverflow.com/a/11908685/1065810

它可能会帮助您理解上面的示例。

简而言之,在您的示例中,类 Docker 保留对 Box 的引用。即使不再需要容器盒,类 Docker 仍然持有对它的引用,从而造成内存泄漏。

让我知道这是否有帮助。

于 2015-05-25T17:30:04.863 回答