【问题标题】:Why is the reference to the Context a memory leak?为什么对 Context 的引用是内存泄漏?
【发布时间】:2017-01-16 23:16:41
【问题描述】:

根据Romain Guy的说法,这种代码很容易发生内存泄漏,因为

.... 视图引用了整个活动,因此 您的活动所持有的任何东西;通常是整个视图 层次结构及其所有资源。

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);

  TextView label = new TextView(this);
  label.setText("Leaks are bad");

  setContentView(label);
}  

我不清楚。
假设应用程序有 1 个活动,这是寿命最长的对象,可以根据需要重新创建。这意味着它的所有实例字段(可以并且通常是Views)在任何时候都可以为空。
并且任何静态实例字段都将与活动本身存在相同的持续时间。
那么我们如何使用上面或下面的代码来获得内存泄漏:

private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);

  TextView label = new TextView(this);
  label.setText("Leaks are bad");

  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);

  setContentView(label);
}

【问题讨论】:

    标签: java android android-activity memory-leaks


    【解决方案1】:

    假设应用程序有 1 个活动,这是寿命最长的对象

    不,不是。您的流程中还有其他对象(例如,Application、内容提供者)会比活动实例寿命更长。

    请特别注意,默认情况下,活动会在配置更改(例如屏幕旋转)时被销毁和重新创建。

    并且任何静态实例字段都将与活动本身存在相同的持续时间。

    没有。只要过程存在,静态字段就存在。您的活动实例可能比这更短。

    那么我们如何使用上面或下面的代码来获得内存泄漏:

    您的第一个示例中没有静态字段。

    Romain Guy 在the blog post that you linked to 中解释了第二种情况:

    这段代码非常快,也非常错误;它泄漏了在第一次屏幕方向更改时创建的第一个活动。当一个 Drawable 被附加到一个视图时,该视图被设置为一个可绘制的回调。在上面的代码 sn-p 中,这意味着可绘制对象具有对 TextView 的引用,该 TextView 本身具有对活动(上下文)的引用,而活动(上下文)又引用了几乎所有内容(取决于您的代码)。

    而且,如果您将 LeakCanary 添加到您的项目中,您会看到泄漏。

    那么,让我们来看看这个:

    • 用户点击与此活动相关的应用的主屏幕启动器图标
    • 您的进程已启动
    • 您的活动实例已创建,然后使用onCreate() 调用
    • sBackgroundnull,因此您将其分配给 getDrawable() 结果
    • 您的活动 UI 出现在屏幕上
    • 作为对打喷嚏的反应的一部分,用户打喷嚏并意外旋转了设备的屏幕
    • 您的旧活动实例已销毁
    • 创建了一个新的活动实例,然后用onCreate() 调用
    • sBackground 不是null,所以你别管sBackground

    你有你的泄漏。正如 Romain 解释的那样,sBackground 对您的 original 活动实例的引用不是很明显。因此,现在您有两个未完成的活动实例:泄露的原始实例,以及由于配置更改而创建的新实例。

    【讨论】:

    • 那么第一个sn-p没有内存泄漏?
    • @Jim:正确。我的猜测是它用于说明紧接在代码 sn-p 之前的句子(“它通常是开发人员传递给需要上下文的类和方法的第一个句子:”),显示将 Context 传递给 @ 987654334@构造函数。
    【解决方案2】:

    TLDR 自 2010 年以来,这不再是内存泄漏。另外,我会说这是 Android 中的错误(内存泄漏)(因为此参考是实现细节),而不是你的应用程序。我们甚至不知道setBackgroundDrawable(不推荐使用setBackground)调用setCallback,它设置了对view 的强引用。 Here 我解释了为什么 Drawable 无论如何都会引用 View


    Romain Guy 在 2010 中对库进行了修复,以通过使用 WeakReference 来防止这导致内存泄漏,因此多年来这一直不是问题:

    【讨论】:

      猜你喜欢
      • 2015-08-04
      • 2016-08-03
      • 1970-01-01
      • 2013-03-23
      • 2014-10-18
      • 2011-10-25
      • 1970-01-01
      相关资源
      最近更新 更多