如果我在单元测试上运行简单方法,它使用非托管资源(例如位图)生成实例,则内存不断增加。在Thread
或ThreadPool
中执行方法的情况下,结果是相同的。但是如果我使用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正常工作,内存不会增长。
答案 0 :(得分:1)
通过这个
阅读可能是值得的Fundamentals of Garbage Collection
在垃圾收集开始之前,所有托管线程都将被挂起 除了触发垃圾收集的线程。
下图显示了触发垃圾的线程 收集并导致其他线程被暂停。
从另一个线程调用Collect可能只对GC有帮助,因为它可以完全挂起工作线程。虽然我知道这并不能解释你的所有结果
然而WaitForPendingFinalizers()工作的原因
是因为它挂起当前线程直到正在处理的线程 终结者队列已清空该队列。
垃圾收集的内部在.Net的不同版本中也发生了很大的变化,你可以使用不同的垃圾收集,我的服务器与工作站
但也许你的其他线程对象会停留太长时间并进入长生命Generation(第1代或第2代),垃圾收集不会经常查看它们。
生存和促销活动。垃圾收集中未回收的对象称为 幸存者,并被提升为下一代。对象那个 幸存下来的第0代垃圾收集被提升为一代 1;促进在第1代垃圾收集中存活的对象 第2代;和在第2代垃圾中存活的物体 收集仍然在第2代。