3

假設我有一個C#方法是這樣的:(顯然不是真正的代碼)方法本地.NET對象何時符合GC要求?

byte[] foo() 
{ 
    var a = MethodThatReturns500mbObject(); 
    var b = MethodThatReturns200mbObject(a); 
    byte[] c = MethodThatReturns150mbByteArray(b); 
    byte[] d = UnwiselyCopyThatHugeArray(c); 
    return d; 
} 

你可以通過命名猜測,由這些方法返回的對象是巨大的。儘管前兩個對象由數百萬個較小的對象組成,而不是像後兩個數組那樣的巨大塊,但每個內存需要數百兆字節的RAM。

我們將盡快將其優化爲一個流式解決方案,但同時我想確保至少我們不會阻止早期對象的GC,同時執行代碼來生成後面的對象。

我的問題是:只要MethodThatReturns200mbObject(a)返回,對象a是否有資格獲得GC?如果沒有,讓GC知道現在有500MB存在的最好方法是什麼?

我的問題的核心是.NET GC確定「這個對象沒有引用」是否足夠聰明,知道MethodThatReturns200mbObject(a)返回後不能引用a。即使var a仍然在理論上可用於後面的代碼,a不在方法的第二行下面的任何地方引用。理論上,編譯器可以讓GC知道a未被引用。但在實踐中,我不確定它是如何表現的。你知道嗎?

+0

'GC.KeepAlive()'不會有多大用處,如果所有的變量都已經保持活着直到其範圍結束。 –

+0

在發佈模式中,是的,編譯器非常聰明,可以在不再使用垃圾收集的時候將'a'標記爲垃圾收集的候選對象。 [這個答案](https://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net/17131389#17131389)可能也有用 – Rob

回答

1

This post explains it with examples.

在理論上,編譯器可以讓GC知道,一個是未引用。但在實踐中,我不確定它是如何表現的。你知道嗎?

正確的答案是,這取決於項目配置 對象是否符合垃圾收集的方法結束 。正如我在什麼時候需要使用GC.KeepAlive中討論的那樣? (它也描述了GC.KeepAlive的用途 - 簡而言之,它是一種引用或「使用」變量的方式,以確保優化器不會優化使用情況),垃圾收集器可能會決定收集只要它們不能被任何 執行代碼使用,就立即生效。這可能會發生在 訪問引用(在編譯時)有效的情況下,但是沒有編寫這樣的 代碼。

但是,在調試模式下編譯和執行代碼時,編譯器 可以防止這種情況發生,以便於調試。其結果是,在 正確執行我們的測試方法,包括預處理 指令:

另一個好讀When do I need to use GC.KeepAlive?

+1

鏈接的答案是一個很好的閱讀,而且正是我在尋找。謝謝! –