C#垃圾收集不会立即生效,具体取决于上下文

时间:2018-01-02 09:51:32

标签: c# garbage-collection

如果我在单元测试上运行简单方法,它使用非托管资源(例如位图)生成实例,则内存不断增加。在ThreadThreadPool中执行方法的情况下,结果是相同的。但是如果我使用Task.Run()运行, GC 正常工作,内存不会增加。

Task.Run与其他情境之间GC操作的区别是什么?

    [TestMethod]
    public void Test()
    {
        //1. GC didn't work.
        Work();

        //2. GC didn't work.
        //new Thread(() => 
        //{
        //    Work();
        //}).Start();

        //3. GC didn't work.
        //ThreadPool.QueueUserWorkItem((obj) =>
        //{
        //    Work();
        //});

        //4. GC didn't work
        //new Action(() =>
        //{
        //    Work();
        //}).BeginInvoke(null, null);

        //5. GC works.
        //Task.Run(() =>
        //{
        //    Work();
        //});

        Thread.Sleep(Timeout.Infinite);
    }

    private void Work()
    {
        while (true)
        {
            GC.Collect();

            var bitmap = new Bitmap(1024, 768);
            Thread.Sleep(10);
        }
    }

第一个案例记忆日志。

  

PrivateBytes:282.0MB,AllHeapsBytes:3.5MB,线程数:32,CPU使用率:12%   PrivateBytes:499.0MB,AllHeapsBytes:2.8MB,线程数:33,CPU使用率:16%   PrivateBytes:734.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:11%   PrivateBytes:959.0MB,AllHeapsBytes:3.0MB,线程数:33,CPU使用率:14%   PrivateBytes:1173.0MB,AllHeapsBytes:3.1MB,线程数:33,CPU使用率:10%   PrivateBytes:1389.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:12%   PrivateBytes:1597.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:9%   抛出异常:' System.ArgumentException'在System.Drawing.dll中   类型' System.ArgumentException'的例外情况发生在System.Drawing.dll中但未在用户代码中处理   参数无效。

第5案例记忆日志

  

PrivateBytes:41.0MB,AllHeapsBytes:3.5MB,线程数:32,CPU使用率:16%   PrivateBytes:41.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:13%   PrivateBytes:41.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:14%   PrivateBytes:41.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:12%   PrivateBytes:41.0MB,AllHeapsBytes:2.9MB,线程数:33,CPU使用率:14%

---更新---

GC.Collect()不会立即收集。当我调用GC.Collect()时,GC将在一个单独的线程上运行每个对象的终结器。

对于GC立即工作,我应该在调用GC.Collect()后添加GC.WaitForPendingFinalizers()。

https://www.developer.com/net/csharp/article.php/3343191/C-Tip-Forcing-Garbage-Collection-in-NET.htm

但我仍然不明白为什么GC在Task.Run环境中工作。

---更新了2 ---

当我在简单的winform项目中运行此代码时,GC正常工作,内存不会增长。

1 个答案:

答案 0 :(得分:1)

通过这个

阅读可能是值得的

Fundamentals of Garbage Collection

  

在垃圾收集开始之前,所有托管线程都将被挂起   除了触发垃圾收集的线程。

     

下图显示了触发垃圾的线程   收集并导致其他线程被暂停。

enter image description here

从另一个线程调用Collect可能只对GC有帮助,因为它可以完全挂起工作线程。虽然我知道这并不能解释你的所有结果

然而WaitForPendingFinalizers()工作的原因

  

是因为它挂起当前线程直到正在处理的线程   终结者队列已清空该队列。

垃圾收集的内部在.Net的不同版本中也发生了很大的变化,你可以使用不同的垃圾收集,我的服务器与工作站

但也许你的其他线程对象会停留太长时间并进入长生命Generation(第1代或第2代),垃圾收集不会经常查看它们。

  

生存和促销活动。垃圾收集中未回收的对象称为   幸存者,并被提升为下一代。对象那个   幸存下来的第0代垃圾收集被提升为一代   1;促进在第1代垃圾收集中存活的对象   第2代;和在第2代垃圾中存活的物体   收集仍然在第2代。