2008-09-17 69 views
11

我在寫一個財務C#應用程序,它接收來自網絡的消息,根據消息類型將它們翻譯成不同的對象,並最終在它們上應用應用程序業務邏輯。如何避免實時.NET應用程序垃圾回收?

的一點是,在應用的業務邏輯後,我很肯定我永遠不會再需要這個實例。而不是等待垃圾收集器釋放它們,我想明確地「刪除」它們。

有沒有更好的辦法在C#這樣做,我應該使用對象池總是重複使用同一組實例或者是有一個更好的策略。

目標是避免垃圾回收在時間關鍵型進程中使用任何CPU。

+0

在.Net中,當(a)創建給定對象速度較慢或(b)在32位應用程序中減少內存碎片時,使用數據的數組/列表/字典等塊> 64 KB,例如>每個元素8K個元素x 8B,或者每個元素16K個元素x 4個B。 .Net **永遠不會重新定位(移動)這樣的大塊**,如果您每次請求不同的大小,這會導致內存碎片化。更安全地分配足夠大的#元素,並保留它們。 (但是在不使用時清除它們的字段,所以GC可以回收它們指向的任何內容。)如果超出分配的大小,請將#元素加倍。 – ToolmakerSteve 2014-03-22 20:34:36

+0

在極端情況下,我有一個32位應用程序(由於依賴關係在64位中不起作用),所以即使我在64位Windows上裝載大量RAM,以避免內存碎片保留列表清單,以便每個子列表適合64 KB,以便.Net不會在「大對象堆」上分配。因此它可以在GC期間「壓縮」內存,而不是在應用程序的4 GB地址空間中創建大型「漏洞」。這個特定的應用程序是有問題的,因爲它包含了很多來自本地(非.Net)內存的DirectX分配。非.Net和.Net分配的混合導致了分裂。 – ToolmakerSteve 2014-03-22 20:41:13

回答

21

不要立即刪除它們。爲每個對象調用垃圾收集器是一個壞主意。通常情況下,你真的不想搞亂垃圾回收器,即使時間關鍵型進程只是在等待發生,如果他們是敏感的競爭條件。

但如果你知道你有VS忙你的應用程序輕載期間,你可能當你到達一個光周期下忙期之前,鼓勵清理嘗試更一般的GC.Collect的()。

+8

只要你明白GC.Collect沒有提供確定性的垃圾收集,這很好。你只是告訴GC運行它的週期,在這種情況下決定是否收集內存。對GC.Collect的調用不保證內存收集。 – JeremiahClark 2008-10-18 03:57:33

+1

改編爲顯示非確定性。 – 2009-02-09 16:49:47

+1

請注意**調用GC.Collect()往往可以做得比弊大於利**:任何仍然有引用的對象將從第0代移至第1代,或者從第1代移至第2代。任何這樣的對象將堅持更長的時間/需要更多GC去除的工作。只有在沒有任何消息仍在工作時調用GC.Collect(),以便沒有任何臨時數據的引用。 – ToolmakerSteve 2014-03-22 20:22:41

1

你可以有在池中的每個類型的實例的數量有限,並重新使用已經用實例來完成。池的大小取決於您要處理的消息數量。

5

強制GC.Collect的()通常是一個壞主意,離開GC做自己最擅長的。聽起來最好的解決方案是使用可以在必要時增長的對象池 - 我已經成功地使用了這種模式。

這樣,您不僅可以避免垃圾回收,還可以避免常規分配成本。

最後,你確定GC導致你的問題?在實施任何節省資源的解決方案之前,您應該測量並證明這一點 - 您可能會造成不必要的工作!

0

從理論上講,如果你的CPU負載很重或者除非真的需要,GC不應該運行。但是如果你必須這樣做,你可能只想把所有的對象都放在內存中,也許是一個單例實例,除非你準備好了,否則不要清理它們。這可能是保證GC運行的唯一方法。

2

如果絕對時間緊迫,那麼您應該使用像C/C++這樣的確定性平臺。即使調用GC.Collect()也會產生CPU週期。

你的問題從你想要節省內存,但擺脫對象的建議開始。這是一個空間關鍵優化。你需要決定你真正想要什麼,因爲GC比人類更適合優化這種情況。

1

並非每次你得到一個消息時創建一個對象的新實例的,你爲什麼不重用已經被使用的對象?這樣你就不會對付垃圾收集器,你的堆內存也不會變得分散。**

對於每種消息類型,都可以創建一個池來容納未使用的實例。每當您收到網絡消息時,都會查看消息類型,將等待的實例從適當的池中取出並應用您的業務邏輯。之後,您將該消息對象的實例放回其池中。

您將很可能希望用實例「延遲加載」您的池,以便您的代碼輕鬆擴展。因此,您的池類將需要檢測何時已經將一個空實例拉出來並填充它,然後再將其分發出去。然後,當調用代碼將它放回池中時,它就是一個實例。

**「對象池是一種使用的模式,它允許對象被重用而不是分配和釋放,這有助於防止堆碎片以及昂貴的GC壓縮。」

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

7

試圖第二猜測垃圾收集器一般是一個非常糟糕的主意。在Windows上,垃圾收集器是a generational one,可以依靠它來非常高效。這個通用規則有一些例外,最常見的是發生一次事件,你知道事實會導致很多舊對象死亡 - 一旦對象被提升到Gen2(最長壽命)他們往往會流連忘返。

在你提到的情況下,你聽起來好像你正在生成一些短暫的對象 - 這將導致Gen0集合。無論如何,這些發生的頻率相對較高,而且效率最高。如果您願意的話,您可以通過擁有可重用的對象池來避免它們,但最好在採取此類行動之前確定GC是否是性能問題--CLR分析器是執行此操作的工具。

應該指出的是,垃圾收集器在不同的.NET框架上有所不同 - 在緊湊框架(運行在Xbox 360和移動平臺上)上,它是非代人的GC,因此您必須多更加小心你的程序產生的垃圾。

11

你自己打了 - 使用一個對象池並重用這些對象。對這些對象的調用的語義需要隱藏在工廠外觀之後。您需要以某種預先定義的方式增加游泳池。也許每當它達到極限時就將其大小增加一倍 - 高水算法或固定百分比。我真的強烈建議你不要調用GC.Collect()。

當池上的負載變得足夠低時,您可以縮小池並最終觸發垃圾回收 - 讓CLR擔心它。

2

從它的聲音看來,您似乎在談論確定性定稿(C++中的析構函數),它在C#中不存在。在C#中最接近的是Disposable模式。基本上你實現了IDisposable接口。

的基本模式是這樣的:

public class MyClass: IDisposable 
{ 
    private bool _disposed; 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if(_disposed)  
      return; 

     if(disposing) 
     { 
      // Dispose managed resources here 
     } 

     _disposed = true; 
    } 
} 
2

如何密集的應用程序?我編寫了一個應用程序,以8KB塊的形式捕獲3個聲卡(Managed DirectX,44.1KHz,立體聲,16位),並通過TCP/IP將3個流中的2個發送到另一臺計算機。 UI呈現音頻電平表和(平滑)滾動3個頻道中的每一個的標題/藝術家。這可以在XP,1.8GHz,512MB等電腦上運行。該應用程序使用大約5%的CPU。

我沒有手動調用GC方法。但我確實需要調整一些浪費的東西。我使用RedGate的Ant分析器來磨合浪費的部分。一個很棒的工具!

我想使用一個預分配的字節數組池,但託管的DX程序集在內部分配字節緩衝區,然後將其返回給應用程序。事實證明,我沒有必要。

5

「目的是爲了避免垃圾收集過程中,時間關鍵進程使用任何CPU」

問:按時間要求嚴格,你的意思是你在聽一些深奧的硬件如果,你不能錯過中斷?

答:如果是這樣,那麼C#不是要使用的語言,您需要的是彙編器,C或C++。

問:如果時間緊迫,你的意思是管道中有很多消息,並且你不想讓垃圾收集器減慢速度?

- 答:如果是這樣,你是不必要的擔心。通過事物的聲音,您的對象非常短暫,這意味着垃圾收集器將非常有效地回收它們,而不會出現任何明顯的性能滯後。但是,唯一可以確定的方法就是測試它,設置它在隔夜處理一個持續的測試消息流,如果你的性能數據可以在GC啓動時發現,我會驚呆的即使你能發現它,如果它真的很重要,我會更加驚訝)。