2010-08-13 88 views
9

我對物體上的垃圾收集過程感到困惑。垃圾收集如何處理對象引用?

object A = new object(); 
object B = A;   
B.Dispose(); 

通過調用處置可變只有B,創建對象將不會被垃圾收集 爲對象仍然是已經A.引用

現在做下面的代碼工作上面一樣?

public static image Test1() 
{ 
    Bitmap A = new Bitmap(); 
    return A; 
} 

現在我從其他方法調用這個靜態函數。

public void TestB() 
{ 
    Bitmap B = Test1(); 
    B.Dispose(); 
} 

靜態函數Test1返回對Bitmap對象的引用。該引用在另一個變量B中保存爲 。通過調用B上的Dispose,B和對象之間的連接會丟失,但是從Test1傳遞的引用會發生什麼情況。在功能TestB的範圍完成之前它會保持活動狀態嗎?

有沒有什麼辦法可以立即處理從靜態函數傳遞來的引用?

+2

Garbage收集不是參考計數。 – 2010-08-13 17:45:45

+0

垃圾收集的重點在於,您不必關心何時或如何釋放內存。原則上,在一個擁有大量內存的系統上,垃圾收集可能不會發生,僅僅因爲在應用程序終止時讓所有內容都被清理起來會更有效率。 – 2010-08-13 18:21:44

回答

15

我可能不在,但您似乎對Dispose和垃圾收集存在誤解。一旦對象的所有引用都以非確定的方式消失,對象將被垃圾收集。 Dispose通常會擺脫非託管資源,因此該對象已準備好進行垃圾回收。在你的第一個例子中,你拋棄了這個對象,理論上它使它不可用,但它仍然存在於堆中,並且你仍然有一個對它的引用,既有A也有B對象。一旦這些對象超出了作用域,垃圾收集器就可以回收這個內存, 但不總是。在示例2中,將位圖A放在堆上,然後返回它的引用,並將B設置爲該引用。然後你處置它,B超出範圍。那時,再也沒有提到它,它將在稍後的時間收集垃圾。

+0

感謝您的回覆。我應該讓我的問題更清楚。我知道通過調用Dispose(),GC不會立即釋放內存,但它會從可終結隊列中移除對象的引用,以便該對象不會移動到下一個Gen。在第二個示例中,我們如何調用dispose關於函數在使用後立即傳遞的引用?如果我們不能處理靜態函數傳遞的引用,它將移動到下一代,並且釋放資源將會有更多延遲。 – kishore 2010-08-13 18:16:29

+1

@kishore:當你說處理參考時,你的意思非常混亂。至少在這種情況下,引用是指向堆上的對象的堆棧上的指針。您在堆上的實例中處置非託管資源。 – 2010-08-13 18:36:28

+0

對不起,我感到困惑。那麼是否有任何方法可以刪除或清除函數傳遞的引用,以便它不會指向堆上的對象? – kishore 2010-08-13 19:25:05

15

Dispose確實不是垃圾收集。你不能明確地垃圾收集一個特定的對象。您可以撥打GC.Collect(),其中請求垃圾收集器運行,但這不一樣。調用Dispose甚至不會將對象從特定變量「斷開連接」,事實上......當該變量保持有效時(直到上次JIT可以檢測到它將再次被讀取),它將防止對象被垃圾收集。

一個對象不會被垃圾收集,直到它不再被任何東西引用爲止。無可否認,這可能比您在某些極端情況下可能會想到的要早,但您很少需要擔心這些。

值得注意的是,Dispose和垃圾收集是非常不同的東西。您致電Dispose發佈非託管資源(網絡連接等)。垃圾收集僅用於釋放內存。可以肯定的是,垃圾收集可以通過終結,這可能會釋放非託管資源作爲最後的手段,但大多數情況下,您應該明確處置非託管資源。

+0

作爲附錄,你永遠不應該依賴終結器,因爲它們可能永遠不會運行。充其量,他們可能是「最後的勝地」,如果該計劃忘記明確處理這些資源,它們將嘗試清理資源。 – siride 2010-08-13 19:33:59

3

Dispose()與垃圾回收沒有任何關係。它所做的只是允許確定性地釋放資源,但你必須明確地調用它。調用Dispose()時,您調用它的對象不會收集垃圾。當所有對它的引用都不存在時,它將有資格進行垃圾回收。

5

碰巧Raymond Chen剛剛寫了一系列描述.NET垃圾收集方面的博客文章。 This post與你的問題最直接相關(什麼時候垃圾收集對象?)。

+0

我期待每年的CLR周:) – 2010-08-13 17:31:53

+0

我打算髮佈一個鏈接,但我今天早上感覺有點懶。他在GC相關主題上有一些很棒的帖子。 – siride 2010-08-13 19:34:21

+0

感謝您分享這篇文章。 – kishore 2010-08-13 21:59:29

1

好的初學者Dispose!=垃圾收集。你可以調用dispose並且從來沒有收集垃圾,因爲「Disposed Object」仍然可以引用它。 dispose方法用於在CG運行之前「清理」對象(關閉打開的數據庫連接或文件連接等)。

object A = new object(); 
object B = A;   
B.Dispose(); 

在這種情況下B.Dispose呼籲一個Dispose方法,因爲B被引用對象的變量A.無論將CGD,因爲他們沒有脫落的範圍呢。

public static image Test1() 
{ 
    Bitmap A = new Bitmap(); 
    return A; 
} 

這裏發生的事情是,你正在創建一個對象並返回,所以當你離開的Test1,A是最有可能被在調用方法的另一個變量引用。這意味着即使你已經離開了這個方法,A仍然是根(很有可能),並且在調用方法完成之前不會被CG'd。

public void TestB() 
{ 
    Bitmap B = Test1(); 
    B.Dispose(); 
} 

這裏B正在創建和調用dispose。這並不意味着它會被收集起來。一旦程序離開方法,B就不在範圍內,這意味着它可以在下次調用GC時收集。

When to use Dispose

2

許多很好的答案在這裏,但我也很喜歡這一點,人們以爲你所需要的IDisposable的原因是,GC應該被命名爲MemoryCollector甚至ManagedMemoryCollector。當收集非託管內存資源(如文件,數據庫連接,事務,窗口句柄等)時,GC並不特別聰明。

其中一個原因是託管對象可能有一個非託管資源需要幾個ram的演出,但對於GC看起來像8個字節左右。

對於文件,db conns等,您通常希望儘快關閉它們以釋放非託管資源並避免鎖定問題。

使用windows句柄我們有線程親和力擔心。由於GC運行在專用線程中,線程始終是釋放窗口句柄的錯誤線程。

因此,GC可以幫助您避免泄漏託管內存並減少代碼混亂,但仍然應儘快釋放非託管資源。

使用()語句是一種祝福。

PS。儘管我沒有任何直接的非託管資源,但通常我會實現IDisposable,但它的進口通知所有實現IDisposable Dispose被調用的成員變量。

0

值得注意的是,調用Dispose可能實際上什麼也不做。它爲對象提供了清理資源的機會,例如數據庫連接和非託管資源。如果您有一個包含非託管資源(如數據庫連接)的對象,則Dispose將告訴該對象是清理這些引用的時間。

垃圾收集的根本問題是,「可以達到這個目標嗎?「只要堆棧上存在對象引用的對象(或者對象層次結構中存在對此對象的引用),該對象將不會被垃圾收集。

示例:

ObjA創建一個ObjB,它創建一個ObjC Obj C不會被垃圾收集,直到它不再被ObjB引用,或者直到ObjB不再被ObjA引用,或者直到沒有保留引用的對象ObjA

同樣,要問的問題是:「該對象目前是否可以被代碼中的任何內容引用?」

+0

我也應該提到,僅僅因爲一個對象沒有被引用,並不意味着它會立即被收集。垃圾收集器根據對象活着的時間長度,它消耗多少內存以及系統中的內存壓力來決定要收集的內容以及時間。 這需要更多地瞭解系統中的垃圾收集器,以真正優化內存使用。但是對於GC中的初學者來說,一旦無法訪問對象,假設內存由垃圾收集器擁有是安全的。 – Jason 2010-08-13 19:25:58

+0

-1因爲這個帖子混淆了GC和Dispose()的目的。的確,Dispose()可以清理非託管資源。但是它被確定性地和明確地由你的代碼調用。它永遠不會被垃圾收集器調用,調用Dispose()永遠不會導致垃圾收集對象。 – siride 2010-08-13 19:36:08