簡體   English   中英

c#中的垃圾收集誤區

[英]Garbage Collection misunderstanding in c#

我搜索谷歌沒有得到我想要的東西。 我不知道我是對還是錯。 看,我試圖了解GC.Collect()所以這里是代碼..

public class SomePublisher
{
    public event EventHandler SomeEvent;
}

public class SomeSubscriber
{
    public static int Count;

    public SomeSubscriber(SomePublisher publisher)
    {
        publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
    }

    ~SomeSubscriber()
    {
        SomeSubscriber.Count++;
    }

    private void publisher_SomeEvent(object sender, EventArgs e)
    {
        // TODO: something
    }
}

我在我的主線程中這樣做..

 SomePublisher publisher = new SomePublisher();

        for (int i = 0; i < 10; i++)
        {
            SomeSubscriber subscriber = new SomeSubscriber(publisher);
            subscriber = null;
        }

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

        Console.WriteLine(SomeSubscriber.Count.ToString());
        Console.ReadLine();

我得到輸出0,但根據我應該是10,因為GC.Collect()必須從內存中刪除class1對象,所以必須調用class1析構函數,所以計數必須增加到10 ..可以任何正文解釋這個..

(另請注意,C#沒有析構函數1 ,它有終結器。這些是非常不同的東西,你不應該混淆它們。)

“問題”就在這條線上:

publisher.SomeEvent += new EventHandler(publisher_SomeEvent);

這將創建一個以特定對象實例的publisher_SomeEvent()方法為目標的委托,並將此委托添加到publisher.SomeEvent事件的調用列表中。 此委托對象引用目標對象,並阻止對象的收集! (這是一件好事 - 如果您將委托委托給特定對象上的方法,那么在不再引用委托之前,您不希望收集該對象。)

這在技術上根本不是問題,而是運行時保持對象仍然存在仍然被引用。

為了說明,這是參考鏈:

SomePublisher -+-> EventHandler --> SomeSubscriber
               |
               +-> EventHandler --> SomeSubscriber
               |
               +-> (Eight more...)

在調用GC.Collect()之前,您需要執行以下兩項操作之一:

  1. 在釋放每個SomePublisher對象之前取消訂閱該事件。 這將使EventHandler委托實例和它們引用的SomeSubscriber實例都有資格進行收集。
  2. 設置publisher = null; 這將導致整個對象圖符合收集條件。

在這兩種情況下,這將釋放對SomeSubscriber對象的所有引用。


1請注意,C#規范確實將這些代碼塊稱為“析構函數”,但這是一個可怕的名稱。 那些熟悉垃圾收集語言的人會對它感到困惑,因為“終結器”是垃圾收集器在不再可以訪問對象時調用的代碼的廣泛術語。 特別是C ++開發人員會期望析構函數在不同的時間執行。 所以是的,C#有一個叫做“析構函數”的東西,但它不是一個析構函數。 (說些什么不是這樣!)

SomeSubcriber對象的終結器直到最后才會被調用。 因為即使SomeSubscriber對象設置為null,它的內存仍然被SomePublisher對象的事件SomeEvent引用,該對象在應用程序結束之前一直存在。 因此,當調用GC.Collect()時,垃圾收集器將找不到要放入終結器隊列的任何對象。

public SomeSubscriber(SomePublisher publisher)
{
// publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}

如果我們可以通過上面的代碼替換SomeSubcriber的構造函數,那么我們將在控制台中將結果作為10。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM