2012-05-02 47 views
0

我很想知道當.NET中的對象超出範圍時會發生什麼。類似這樣的:局部範圍後的'對象'會發生什麼?

class A 
{ 
    ClassB myObjectB = new ClassB(); 
} 
//what happens to object at this point here? 

在內存中會發生什麼? GC在超出範圍並在堆中放棄參考時會被調用嗎?

+3

GC將對象「在不再可達到的某個點之後」收回。然而,這是一個不好的例子,因爲'myObjectB'只在一個'new A'(永遠不會發生)時才被賦值,並且「a」將保持由myObjectB命名的對象強烈可達,只要「a」本身是強烈可及的。 – 2012-05-02 23:10:43

+0

「如果某個線程無法遍歷任何[soft/weak/phantom]引用對象[可以從根]到達它,那麼該對象是*強烈可達*。」 (這裏是[Java中的位](http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html),但它也適用於C#,減去一些參考「強度」水平。) – 2012-05-02 23:20:07

+0

這段代碼甚至不編譯。 – Aliostad

回答

3

內存中會發生什麼? GC在超出範圍並在堆中放棄參考時會被調用嗎?

否 - 此時GC不會被調用。

會發生什麼事是對象留在內存中,但現在是「無根」 - 基本上,沒有任何其他對象引用該對象。因此,該對象現在是合格垃圾收集。

在未來的某個時候,GC會運行。發生這種情況時,無根對象現在將符合垃圾回收的條件。根據哪一代持有對象,它可能會或可能不會被清理。最終,如果程序繼續執行,並且內存壓力導致收集適當的一代,則該對象的內存將被回收。

沒有確切的時間點何時發生(除非你明確地呼籲GC.Collect,這是一個壞主意)。但是,使用託管代碼,您並不需要擔心這種情況何時發生。假設它會在您的特定應用程序適用時發生。

+0

在Java中,這些術語是強烈可及的(並且無法訪問):在談論C#/ CLR時,您是否知道使用任何此類「權威」術語? – 2012-05-02 23:21:48

+0

@pst我相信該規範使用「紮根」,這就是爲什麼我使用該術語。這是對象的「正常」句柄[vs.一個弱或固定的句柄,在上面的示例中](請參閱GCHandle:http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle.aspx) –

+0

@pst C#語言規範使用「未使用對象「(第1部分,簡介)引用無根對象....現在查找CLI規範術語。 –

1

如果沒有引用該對象,它最終會被GC收集。事情是,你無法預測到底什麼時候會發生。你只知道,它會發生。

+0

但我的答案是什麼時候? –

+0

@Raj,這正是問題所在。你不能確定與c#。這不是C++,您可以在其中控制應用程序的每一個位置。如果你需要這種行爲,也許C#不適合你。其他人用更多的話來解釋它,但原理仍然是一樣的。可能對你的問題最準確的答案是:「最終」。 – walther

+0

明白了,謝謝! –

2

我發現「超出範圍」是一種更加C++特定的事物思考方式,在給定作用域的末尾釋放一個具有自動存儲的對象並釋放它的析構函數。

在C#世界中,沒有「超出範圍」。變量(讀取:名稱)存在於某個範圍內,就是這樣。 GC真的不擔心這一點;一個對象可以在其名稱範圍的結尾甚至退出之前收集,或者之後很長時間,這取決於它是什麼引用以及GC何時決定收集是必要的。

這兩個概念應該分開離婚和推理。範圍界定關於名稱,而垃圾收集只關心對象的可達性。當一個對象不再可以從其中一個已知的根中訪問時,它將被計劃收集。

+0

嗯,是且不是 - (在這裏刪除方程中的優化)在上面的例子中,該對象在引用超出範圍之前(或被設置爲引用不同的對象或null)不能變爲無根。我認爲說這兩個概念完全分離是不公平的 - 但它與C++的思想意義完全不同。 –

+0

@ReedCopsey但你爲什麼要從等式中去掉優化?大部分代碼都經過優化 – phoog

+0

由於在沒有進一步限定的情況下,它的名字被限制在那裏,因此該參考文件在課程以外的任何地方都是「超出範圍」。我認爲你將生命週期與範圍混爲一談,但我相信C#在兩者之間做了一定的區分。請參閱:「•由類成員聲明(第10.2節)聲明的成員的範圍是聲明發生的類主體。另外,類成員的範圍擴展到派生類的範圍包含在成員的可訪問性域(第3.5.2節)中的類「。 – MikeP

3

從PST的評論之後,一個更好的例子可能是這樣的:

void M() 
{ 
    ClassB myObjectB = new ClassB(); 
} 
//what happens to object at this point here? 

在這個例子中,myObjectB是一個局部變量,而不是一個字段。那麼,當局部變量超出範圍時會發生什麼? 沒什麼!範圍與C#中的對象生命週期無關。

真正發生的是,JIT編譯器決定在某個時候釋放對象如果該變量不會在方法的其餘部分中使用,那麼在變量範圍結束之前可以是。一旦該對象不再被引用,如其他答案也提到,它將變爲合格由GC收集。直到GC運行(實際上,直到GC收集對象所處的世代)之前它才真正被收集。

作爲PST暗示,場是差的例子,因爲它總是會到達每當其包含的對象是可到達的,所以範圍和對象的生存期之間的間隔甚至更大:

class A 
{ 
    private object o = //whatever 
} 

void B() 
{ 
    var a = new A(); 
    // here, the field o is not in scope, but the object it refers to is reachable and cannot be collected. 
    GC.KeepAlive(a); 
} 
1

一般來說,垃圾收集發生在3代(0,1或2)。何時收集這些數據取決於操作系統需要多少資源。

對GC.Collect()的調用將收集所有可用資源,但可以定義要收集哪一代資源。