簡體   English   中英

C#:釋放 memory 使用

[英]C#: Releasing memory usage

對於開箱即用的問題,我需要執行一個長期繁重的過程。 所以我把這個過程分成了多個子過程。 現在的問題是如何在每個 window 執行之前釋放 memory。

用一個例子更容易解釋。 讓我們看一下這個偽代碼。

1. Some earlier code to do other things
2. Do
3.     Raise a Task
4.     If raised-task > 1000
5.         Wait all raised task to finish
6.         Release Memory
7.     End If
8. Wile Something not relevant

有了這個想法,我開發了下一個方法,每次達到線程限制時都會執行該方法:

List<Task> LTask();
//It's not relevant, but this list is populate like
//var task = Task.Run(() => something());
//LTask.Add(task);

private void waitForAll()
{
    //Break point 1
    Task.WhenAll(LTasks).Wait();

    LTasks.Clear();
    LTasks = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();

    //Break point 2
    LTasks = new List<Task>();
}

我預計 memory 在某些值附近會保持不變(有一些變化)。 我是說:

  1. 已達到線程限制
  2. memory 在 BreakPoint 1 中使用 Visual Studio 診斷工具的快照 --> 100MB
  3. 在 BreakPont 2 --> 100 MB 中使用 Visual Studio 診斷工具的 memory 使用快照。 第一個問題,為什么沒有減少? 所有線程都完成了,我強制垃圾收集器執行。

下次達到限制並執行此代碼時,如果我再次拍攝快照,memory 會不斷增加:200、300、...

這是診斷工具的捕獲。 每次到達斷點 1 時拍攝奇數快照,並在斷點 2 處拍攝偶數快照。

在此處輸入圖像描述

第二個問題,這將繼續無限制地增加,直到拋出Out of memory Exception

最后一個問題,有什么辦法可以解決這個問題,並發布memory?

更新 1:經過一些測試,感謝評論,我開發了一個測試代碼來深入研究它。 必須涉及其他事情。 請看下一段代碼。 memory 繼續無限制地增加。

private List<Task> LTasks = new List<Task>();
private void manageThreadholdLimit()
{
    waitForAll();
    realeaseMemory();
}

private void waitForAll()
{
    Task.WhenAll(LTasks).Wait(); 
    LTasks.Clear();
    LTasks = null;  
}
private void realeaseMemory()
{   
    GC.Collect();
    GC.WaitForPendingFinalizers();

    LTasks = new List<Task>();
}
public void Main(){
    int i = 0;

    while (true)
    {
        i++;

        var task = Task.Run(() => Thread.Sleep(100));
        LTasks.Add(task);

        //Si hemos alcanzado el máximo de paralelismo, esperamos la conclusión
        if (i % 1000 == 0) manageThreadholdLimit();

    }
}

GC 收集在調試中略有不同,請參閱:( John Skeet 知道所有)所以我會在發布模式下運行它時分配日志以驗證所有行為。

任何解決方案都將非常依賴於您的實際代碼以及是否有非托管資源被訪問。

也就是說,我以前必須處理過這樣的問題,並且我以前曾以兩種不同的方式“解決”過它。

一種解決方案

在執行實際工作的 class 中,讓計數器在構造函數中遞增並在終結器中遞減,並等待計數器低於定義的閾值,重要的是再次運行 collect 以收集最終對象。

在繼續之前請仔細檢查 memory 的總消耗量,否則您可能會遇到 memory 異常。

現在這實際上會稍微增加您的 memory 消耗。 有關更多信息, 請參閱

另一種解決方案

通過使用 GC.GetTotalMemory() 甚至更好的性能計數器,有一個循環等待 memory 消耗下降,然后等待它下降。

如果您的資源沒有被收集,這最終可能根本不做任何工作。

不保證在您取消引用 object 后GarbageCollector將立即運行。 事實上,它可能不會。 即使您想手動調用它以使用GC.Collect()進行測試,您也不能真正保證它會立即運行。 此外,經常調用 GC 也是有代價的。 可以使用 RAM(至少在具有大量 RAM 的較新機器中......)。 如果您的 memory 上升,並在較長時間后保持上升,您就會遇到問題。 這通常表明您還有其他一些 memory 問題。 也許你有泄漏?

如果我們談論的是免費解決方案,您可以使用ProcessExplorerCLR Profiler來查找潛在的 memory 問題。 是一篇關於如何做到這一點的文章。

需要注意的事項:

  1. 盡快將對象的引用設置為null 你完成了清單嗎? 將其值設置為null
  2. 將處理大量數據的方法拆分為更小的方法 - 即使您將某些內容設置為null ,GC 也不會在方法退出之前運行。
  3. 檢查您是否在需要的地方正確實施了終結器。
  4. 確保您沒有泄漏:檢查您正在使用的對象是否沒有從工作方法外部引用。 對事件處理程序和數據綁定要特別小心。

此外, Task.WhenAll()顯然保留了對其所有“子”任務的引用。 如果您以相同的方法立即手動調用 GC,我認為它可能無法觸及 memory,因為方法本身仍在“引用”它。

是一篇關於垃圾收集的 MSDN 文章。

我想我找到了一個解釋,感謝@ScottChamberlain 的回答: Why Garbage Collector doesn't collect Tasks objects

我認為 memory 會增加,因為 TaskScheduler 是 static,並且仍然存儲任務的引用,盡管任務已完成。 因此,引用的數量不斷增加 memory,而不是每個任務本身的 memory 使用量。

我還沒有弄清楚如何刪除這些引用,但作為替代解決方案,我可以重構使用線程的代碼。 例如,此代碼是我問題中“更新 1”的替代方法。 memory 在 18mb 上保持穩定。

private static List<Thread> LThreads = new List<Thread>();
private static void manageThreadholdLimit()
{
    waitForAll();
    realeaseMemory();
}

private static void waitForAll()
{
    LThreads.ForEach(x => x.Join());
    LThreads.ForEach(x => x = null);
    LThreads.Clear();
    LThreads = null;
}
private static void realeaseMemory()
{


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

    LTasks = new List<Task>();
    LThreads = new List<Thread>();
}
public static void Main(string[] args)
{
    int i = 0;

    while (true)
    {
        i++;

        var t = new Thread(() => Thread.Sleep(100));
        LThreads.Add(t);
        t.Start();

        if (i % 1000 == 0) manageThreadholdLimit();

    }
}

暫無
暫無

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

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