为什么对Context的引用是内存泄漏?

Jim*_*Jim 4 java android memory-leaks android-activity

根据Romain Guy的说法,这种代码很容易因内存泄漏而导致

......观点提到了整个活动,因此也提到了你的活动所持有的任何内容; 通常是整个View层次结构及其所有资源.

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

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

  setContentView(label);
}  
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

Com*_*are 10

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

不它不是.您的流程中还有其他对象(例如Application,内容提供商)将比活动实例更长久.

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

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

不会.只要流程在周围,静态字段就会出现.您的活动实例可能比这更短.

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

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

Romain Guy解释了您链接到的博客文章中的第二个场景:

这段代码非常快,也非常错误; 它泄漏了第一次屏幕方向更改时创建的第一个活动.将Drawable附加到视图时,视图将设置为drawable上的回调.在上面的代码片段中,这意味着drawable具有对TextView的引用,TextView本身具有对活动(Context)的引用,而Activity又引用了几乎所有内容(取决于您的代码).

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

那么,让我们来看看:

  • 用户点击您应用的主屏幕启动器图标,该图标与此活动相关联
  • 您的流程已开始
  • 您的活动实例已创建,然后随之调用 onCreate()
  • sBackgroundnull,所以你给它分配getDrawable()结果
  • 您的活动UI将显示在屏幕上
  • 使用者打喷嚏并意外地旋转装置的屏幕,作为对打喷嚏起反应的一部分
  • 您的旧活动实例已被销毁
  • 创建一个新的活动实例,然后调用它 onCreate()
  • sBackground 是不是null,所以你离开sBackground独自

你有泄漏.正如Romain所解释的那样,sBackground原始活动实例有一个非常明显的引用.因此,现在您有两个此活动的未完成实例:泄漏的原始文件,以及由于配置更改而创建的新原始实例.