2010-01-27 135 views
19

我對C#中只有引用類型被垃圾收集的事實感到有點困惑。 這意味着GC僅挑選存儲器取消分配的參考類型。 那麼值類型會發生什麼,因爲它們也會佔用堆棧中的內存?在C#中銷燬結構對象?

回答

29

一開始,無論他們是堆的堆或部分依賴於什麼情況下他們的一部分 - 如果他們是引用類型之內,他們將在堆上反正。 (你應該考慮多少你真的關心棧/堆鴻溝無論如何 - 作爲埃裏克利珀寫,這是largely an implementation detail

然而,當上下文被收回基本值類型的內存被回收 - 所以即使堆棧你從方法返回時彈出,「回收」整個堆棧幀。同樣,如果值類型值實際上是對象的一部分,那麼當對象被垃圾收集時,內存將被回收。

簡短的回答是,你不必擔心它:)(這裏假設你沒有任何東西其他比內存擔心,當然 - 如果你已經有了引用的結構對需要釋放原生手柄,這是一個有些不同的情景。)當堆棧幀被刪除,已執行後,我會承擔

+0

感謝您參考此綜合性文章。我發現Eric Lippert的全局指令與整個c#哲學非常一致:「只關注語義,我們會爲你處理其餘的問題」 – 2013-12-10 15:32:34

3

堆棧中的值類型超出範圍時將從堆棧中刪除。

3

值類型一旦超出範圍就會被銷燬。

0

還要補充一點,堆在一個線程級別,並且堆在應用程序域級別。

所以,當一個線程結束,將通過RECLAM特定線程使用的堆棧存儲器。

7

有在這個問題上使用,比如破碎,回收,重新分配,除去過多的動詞。這與實際發生的情況並不相符。局部變量根本不復存在,Norwegian parrot style

的方法,具有一個入口點,即首先發生的事情是,CPU堆棧指針調整。創建一個「堆棧框架」,即本地變量的存儲空間。 CLR保證這個空間被初始化爲0,而不是因爲明確的分配規則而在C#中強烈使用的功能。

的方法有出口的一個點,即使你方法的代碼穿插着多個return語句。此時,堆棧指針會簡單地恢復到其原始值。實際上,它「忘記」那裏的局部變量。它們的值不以任何方式「擦除」,字節仍然存在。但是它們不會持續很長時間,您的程序中的下一次呼叫將會再次覆蓋它們。 CLR零初始化規則確保您永遠不會觀察那些不安全的舊值。

非常非常快,只需要一個處理器週期。這種行爲在C#語言中的可見副作用是值類型不能有終結器。確保不需要做額外的工作。

+0

這個局部變量已經不存在了!它已不復存在!這是一個前變量。 :) – 2015-12-05 08:03:08

17

我對C#中只有引用類型被垃圾收集的事實感到困惑。

這不是事實。或者說,這個陳述的真實性或虛假性取決於你的意思是「收集垃圾」。垃圾收集器在收集時肯定會查看值類型;這些值類型可能還活着,並持有到引用類型:

struct S { public string str; } 
... 
S s = default(S); // local variable of value type 
s.str = M(); 

在垃圾收集器運行時,它看起來確實在s,因爲它需要確定s.str還活着。

我的建議:澄清精確地您的動詞「獲取垃圾收集」意味着什麼。

GC僅挑選存儲器取消分配的參考類型。

同樣,這不是事實。假設有的

class C { int x; } 

爲整數存儲器中的實例將是對垃圾收集堆,並且因此被垃圾收集器回收時C的實例變爲無根。

爲什麼你認爲只有引用類型的內存被垃圾收集器釋放的假?正確的說法是被垃圾回收器分配爲的內存是,垃圾回收器取消分配了,我認爲這很有道理。 GC分配它,所以它負責清理它。

那麼值類型會發生什麼,因爲它們也會佔用堆棧中的內存?

沒有什麼事情發生在他們身上。沒有什麼需要發生在他們身上。該堆棧是一百萬字節。線程啓動時確定棧的大小;它始於一百萬字節,並且在整個線程執行期間它保持一百萬字節。堆棧中的內存既不被創建也不被破壞;只有其內容被改變。

+26

「堆棧中的內存既不被創建也不被破壞」 - 我建議我們稱之爲** Lippert內存保存法則**。 :) – 2010-01-27 19:11:26

0

.NET中的每個值類型實例都是其他內容的一部分,可能是更大的封閉值類型實例,堆對象或堆棧幀。每當這些事情產生時,其中的任何結構也會產生;那麼只要包含它們的東西確實存在,那些結構就會繼續存在。當包含結構的東西不復存在時,結構也會如此。在沒有破壞容器的情況下,沒有辦法摧毀一個結構,並且無法銷燬包含一個或多個結構的東西而不破壞其中包含的結構。