2015-03-13 37 views
0

我試着去了解,爲什麼會有不同的行爲。Finilize and GC.Collect

代碼1不同代碼2只是註釋行Console.WriteLine(h.ToString());

但是,在這種情況下Console.Beep();代碼1執行以前static void Main(string[] args)完成。

代碼2Console.Beep();只有當static void Main(string[] args)完成(進程終止?)時才執行。

請問您能解釋一下,爲什麼會這樣?

我試圖用[+/-]優化設置調用代碼 - 看起來不依賴它。

現在我沒有WinDbg, - mb在反編譯代碼中的答案。

代碼1:

class Program 
{ 
    static void Main(string[] args) { 
     var h = new Haha(); 

     // Console.WriteLine(h.ToString()); 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     /* Console.Beep() calls here */ 

     Console.ReadKey(); 
    } 
} 

public class Haha 
{ 
    ~Haha() { 
     Console.Beep(); 
    } 
} 

代碼2:

class Program 
{ 
    static void Main(string[] args) { 
     var h = new Haha(); 

     Console.WriteLine(h.ToString()); 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     Console.ReadKey(); 

     /* Console.Beep() calls here */ 
    } 
} 

public class Haha 
{ 
    ~Haha() { 
     Console.Beep(); 
    } 
} 
+0

您是否在調試器下運行代碼? – 2015-03-13 10:55:46

回答

1

在第一個例子:

var h = new Haha() 

變量沒有在任何地方引用的,所以它被移除。通過這種方式,Haha()在創建之後就不會被引用,因此可以在任何時候使用GC。

在第二個示例中,變量h被引用,因此它具有引用塊的「最大」生命週期(因此整個main)。編譯器和GC可以檢測到h的「最後」使用,並認爲它在Console.WriteLine(最後一次使用h)(縮短它的生命週期)之後沒有被引用,但顯然不這樣做。

請注意,如果您以發佈模式運行程序+運行時沒有調試器,則代碼2會給出(至少它給我的)與代碼1相同的結果。調試器爲了更容易調試,保證所有的引用都是有效的,直到聲明塊的末尾(見https://stackoverflow.com/a/7165380/613130)在Release模式下運行程序+在沒有調試器的情況下運行,我們解決了這個問題。在沒有PDB只調試信息(屬性 - >建設 - >高級 - >調試信息):

做了一些測試:有你需要有更短的壽命(同時)再加上不帶調試器的程序(Ctrl + F5)。如果將「調試信息」設置爲「全部」,則會在程序集中注入DebuggableAttribute,並且CLR不會嘗試縮短變量的使用期限。如果使用調試器運行,也是如此。如果通過命令行編譯,設置選項/debug等於/debug:full(請參閱https://msdn.microsoft.com/en-us/library/8cw0bt21.aspx

+0

明智的答案,謝謝!我知道'保證所有的引用都是有效的,直到它們被聲明的塊的末尾,如果程序運行調試器,但我錯了 - 我認爲有/沒有調試操作配置(調試/發佈版本),但,實際上,它是通過'Run' /'Run Without Debugger'運行的(F5/Ctrl + F5) – FSou1 2015-03-13 13:56:29