内存消耗代码优化,垃圾收集器理论

时间:2010-10-15 14:17:19

标签: c# wpf optimization memory destructor

在我的WPF应用程序中,我按以下方式调用新窗口:

_newWin = new WinWorkers_AddWorker();
_newWin.WindowState = this.WindowState;
_newWin.Show();

_newWinprivate Window object

我的问题是,在致电_newWin后,我应该为_newWin.Show()分配空值吗?

这会减少内存消耗,因为垃圾收集器/析构函数会更早地清理空值对象吗?

感谢。

3 个答案:

答案 0 :(得分:6)

将值设置为null通常无关紧要。它很少有用。它偶尔会有害。

让我们先考虑最简单的情况:

private void DoStuff()
{
  var newWin = new WinWorkers_AddWorker();
  newWin.WindowState = this.WindowState;
  newWin.Show();
  int irrelevant = 42;
  this.whoCares = irrelevant * 7;
  int notRelevantEither = irrelevant + 1;
  this.stillDontCare = notRelevantEither * irrelevant;
}

此处newWin仅存在于此方法中;它是在它中创建的,并且不会通过返回或分配给具有更宽范围的成员而离开方法的范围。

newWin收集垃圾时会询问很多人,他们会告诉您它会在this.stillDontCare行之后发生,因为那时newWin超出了范围。因此,我们可以在最后一次使用后分配newWin = null稍微获胜,但它可能微不足道。

从概念上讲,这是正确的,因为我们可以在此之前的任何地方添加处理newWin的代码,newWin可供我们使用。

事实上,newWin很可能在.Show()之后立即有资格收集。虽然它在概念上处于范围之后,但它实际上并未使用,编译器知道这一点。 (从现在开始,通过“编译器”,我将指的是产生实际运行代码的整个过程,结合IL编译器和抖动)。由于newWin本身使用的内存(即堆栈上的引用,而不是对象)不再使用,编译器可以将该内存用于irrelevant或其他内容。没有实时参考,该对象有资格收集。

实际上,如果调用对象的最后几个方法实际上并不使用this指针(无论是直接使用还是使用成员字段),那么甚至可以在调用这些方法之前收集对象,因为它们是实际上并没有使用该对象。如果你有一个方法,其this指针从未被使用(再次,直接或间接),那么它可能永远不会被创建!

现在,考虑到这一点,我们可以看到,如果我们在变量超出范围之前为变量赋值null,那么它实际上不会产生甚至可能产生的微小差别。

实际上,分配可能甚至可能需要更长时间才能符合条件,因为如果编译器无法看到变量的使用不会影响对象(不太可能,但也许它如果有try...catch...finally个区块使得分析更复杂,则可能发生这种情况,然后它甚至可能延迟对象被认为合格的点。这可能是微不足道的,但它就在那里。

到目前为止这么简单;如果我们独自离开,好的事情就会发生,而单独离开也很容易。

然而,引用可以从设置为null中受益。考虑:

public class SomeClass
{
  private WorkerThing _newWin;
  private void DoStuff()
  {
    _newWin = new WinWorkers_AddWorker();
    _newWin.WindowState = this.WindowState;
    _newWin.Show();
  }
}

在此考虑,在调用DoStuff()之后,_newWin存储在成员变量中。在SomeClass的实例超出范围之前,它不会超出范围。什么时候会发生?

好吧,我无法回答这个问题,但有时答案很重要。如果SomeClass本身也是短暂的,那么谁在乎呢。它很快就会超出范围,用它来_newWin。但是,如果我们分配了_newWin = null,那么该对象将立即有资格进行收集。

现在,对此有一些重要的警告:

  1. 首先,_newWin没有充分理由成为成员变量。如果上面的示例是完整的代码,我们会将其移回DoStuff()本地,并且不仅以这种效率方式获得,而且更多,更重要的是我们的正确性,我们不能对来自其他成员的_newWin做些蠢事。
  2. 如果我们在成员变量中持有某些东西,那可能是有充分理由的。这个充分的理由将会超越尽可能快地清理变量的狂热。
  3. 无论如何,大多数物体本身并没有占用那么多记忆。这里或那里的成员变量不会受到伤害。
  4. 因此,将null赋给成员变量的主要原因仅仅是因为null已成为最合适的值。将null分配给不再使用的成员通常不会尽快释放其内存,但因为它不再适合使用,并且这变得不可能 - 并且明确地向其他代码发出信号 - 当它发生时是空的。

    如果引用的持续时间比方法长(因此放入成员变量)比包含对象的寿命短得多内存量,然后它可能是分配null将开始有意义。在这种组合发生的非常罕见的情况下,我们可能希望将它指定为null,以表明它不再适用于类,无论如何都要使用,所以我们仍然不会指定null ,目的是将其发布到GC。这几乎是可能的,但真的是“不”。

答案 1 :(得分:2)

垃圾收集不会清除空对象。如果您设置对null的引用,则只需删除指向对象的引用,以便实际降低其保留计数器。

但是当一个对象超出范围而无法通过代码回收时,这个计数器也会减少。所以你要做的就是没用。

GC会选择仅在不再引用它时才释放它,但是如果你显示那个窗口,你肯定会在某个地方引用它。

编辑:如评论中所述,引用计数可能不是.NET vm的方式(对不起但我不使用M $平台)但原则保持不变。无论如何,您的窗口都不会被GC,因为它是可见的。

答案 2 :(得分:0)

它不会删除对象,因为它将在应用程序的其他地方引用(在内部代码中)否则,将_newWin的值设置为null并将其收集垃圾将使您的窗口消失(并可能导致程序崩溃) ),这不会发生。