簡體   English   中英

C#動作和GC

[英]C# Actions and GC

我在C#中使用Actions,我想知道在希望GC正確收集對象后是否需要將Action的實例設置為null? 這是一個例子:

public class A
{
 public Action a;
}

public class B
{
  public string str;
}

public class C
{
 public void DoSomething()
 {
   A aClass = new A();
   B bClass = new B();
   aClass.a = () => { bClass.str = "Hello"; }
 }
}

在我的Main方法中我有這樣的東西:

public void Main(...)
{
  C cClass = new C();
  cClass.DoSomething();

  Console.WriteLine("At this point I dont need object A or B anymore so I would like the GC to collect them automatically.");
  Console.WriteLine("Therefore I am giving GC time by letting my app sleep");
  Thread.Sleep(3000000);
  Console.WriteLine("The app was propably sleeping long enough for GC to have tried collecting objects at least once but I am not sure if A and B objects have really been collected");
 }
}

請閱讀Console.WriteLine文本,它將幫助您理解我在這里問的問題。

如果我將GC的理解應用於此示例,則GC將永遠不會收集對象,因為A不能被銷毀,因為它包含B的實例。我是對的嗎?

我該如何正確收集這兩個對象? 我是否需要將Actions的實例設置為null以便讓GC在應用程序結束之前收集對象,或者GC是否已經知道如何銷毀具有諸如A和B之類的操作的對象的某種非常智能的機制?

編輯:問題是關於GC和正確收集對象。 它不是調用方法collect()。

這個問題有很多問題。 而不是直接回答你的問題,我將回答你應該問的問題。

讓我們先解讀你對GC的看法。

長時間睡覺會激活垃圾收集器嗎?

沒有。

是什么激活了垃圾收集器?

出於測試目的,您可以使用GC.Collect()GC.WaitForPendingFinalizers() 僅將它們用於測試目的; 除了在一些非常罕見的情況下,在生產代碼中使用它們是一種不好的做法。

在正常情況下,觸發GC的事情很復雜; GC是一種高度調整的機器。

就封閉的外部變量而言,垃圾收集的語義是什么?

被轉換為代表的λ的封閉在外層變量的壽命 延長不小於代表的壽命短

假設我有一個Action類型的變量,它使用lambda初始化,該lambda在引用類型的外部局部變量上關閉。 為了使該變量引用的對象可以收集,我是否必須將Action類型的變量設置為null

在絕大多數情況下,沒有。 垃圾收集器很聰明; 讓它做它的工作,不要擔心它 最終,運行時將確定任何實時根都無法訪問Action變量,並使其可以進行收集; 封閉的外部變量將符合條件。

在極少數情況下,您希望盡快丟棄對Action引用,但它們很少見; 絕大多數時候,讓GC不受干擾地完成工作。

是否存在外部變量的壽命延長太久的情況?

是。 考慮:

void M()
{
    Expensive e = new Expensive();
    Cheap c = new Cheap();
    Q.longLived = ()=>c; // static field
    Q.shortLived = ()=>e; // static field
}

執行M()會為兩個代理創建一個閉包。 假設shortLived很快將被設置為null ,並且longLived被設置為null 不幸的是, 兩個局部變量的生命周期都延長到longLived引用的對象的生命周期,即使只有c仍然可以訪問。 longLived的引用已經死亡之前,不會釋放昂貴的資源e

許多編程語言都有這個問題; JavaScript,Visual Basic和C#的一些實現都存在這個問題。 有一些關於在C#/ VB的Roslyn版本中修復它的討論,但我不知道這是否會實現。

在這種情況下,解決方案是首先避免這種情況; 如果其中一個代表的生活時間比另一個代表長得多,那么就不要讓兩個lambdas共享一個閉包。

在什么情況下, 不是封閉的外部變量的本地變得可以收集?

運行時可以證明本地無法再次讀取的那一刻 ,它引用的東西變得可以收集(假設本地是當然唯一的根。)在你的示例程序中,沒有要求aClass中的引用和bClass保持活着直到方法結束。 實際上,有一些罕見但可能的情況,GC可以在一個線程上解除分配一個對象, 而它仍然在另一個線程的構造函數中 GC可以非常積極地確定什么是死的,所以要小心。

面對激進的GC,我如何保持活力?

GC.KeepAlive()當然。

我在C#中使用Actions,我想知道在希望GC正確收集對象后是否需要將Action的實例設置為null?

不必要。 只要沒有引用該委托的對象,該委托就有資格獲得GC。

話雖如此,在您的示例中, aClassbClass仍然是有效變量,並引用可到達的對象。 這意味着aClass.a仍然可以訪問,並且不符合GC的條件,因此不會收集它。

如果您希望將這些文件進行垃圾回收,則需要將對象引用( aClass )顯式設置為null,以便A實例及其包含的委托不再是可訪問的對象,然后您必須顯式調用GC.Collect以觸​​發GC,因為沒有任何東西會導致GC在您的代碼中觸發。

暫無
暫無

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

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