我遇到了一个问题,似乎在异步等待方法中,如果本地资源在垃圾回收期间没有被清除,则可能会出现问题。
我创建了一些示例代码来说明这个问题。
SimpleClass
SimpleClass使用静态计数器来记录活动实例的数量,通过在构造期间递增静态字段_count并在销毁期间递减相同字段来实现。
using System;
namespace AsyncGarbageCollector
{
public class SimpleClass
{
private static readonly object CountLock = new object();
private static int _count;
public SimpleClass()
{
Console.WriteLine("Constructor is called");
lock (CountLock)
{
_count++;
}
}
~SimpleClass()
{
Console.WriteLine("Destructor is called");
lock (CountLock)
{
_count--;
}
}
public static int Count
{
get
{
lock (CountLock)
{
return _count;
}
}
}
}
}
程序
这里是主程序,它有三个测试:
- 标准调用,它初始化类,然后将变量留给超出范围
- 异步调用,在初始化类后将变量留给超出范围
- 异步调用,在初始化类后将变量设置为 null 然后将其留给超出范围
在每种情况下,变量都会在 GC.Collect 被调用之前超出范围。因此,我预计在垃圾回收期间将调用析构函数。
using System;
using System.Threading.Tasks;
namespace AsyncGarbageCollector
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press 1, 2 or 3 to start.\n\n");
var code = Console.ReadKey(true);
if (code.Key == ConsoleKey.D1)
RunTest1();
else if (code.Key == ConsoleKey.D2)
RunTest2Async().Wait();
else if (code.Key == ConsoleKey.D3)
RunTest3Async().Wait();
Console.WriteLine("\n\nPress any key to close.");
Console.ReadKey();
}
private static void RunTest1()
{
Console.WriteLine("Test 1\n======");
TestCreation();
DisplayCounts();
}
private static async Task RunTest2Async()
{
Console.WriteLine("Test 2\n======");
await TestCreationAsync();
DisplayCounts();
}
private static async Task RunTest3Async()
{
Console.WriteLine("Test 3\n======");
await TestCreationNullAsync();
DisplayCounts();
}
private static void TestCreation()
{
var simple = new SimpleClass();
}
private static async Task TestCreationAsync()
{
var simple = new SimpleClass();
await Task.Delay(50);
}
private static async Task TestCreationNullAsync()
{
var simple = new SimpleClass();
await Task.Delay(50);
Console.WriteLine("Setting Null");
simple = null;
}
private static void DisplayCounts()
{
Console.WriteLine("Running GC.Collect()");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Count: " + SimpleClass.Count);
}
}
}
结果
Test 1
======
Constructor is called
Running GC.Collect()
Destructor is called
Count: 0
Returned to Main
Running GC.Collect()
Count: 0
Test 2
======
Constructor is called
Running GC.Collect()
Count: 1
Returned to Main
Running GC.Collect()
Destructor is called
Count: 0
Test 3
======
Constructor is called
Setting Null
Running GC.Collect()
Destructor is called
Count: 0
Returned to Main
Running GC.Collect()
Count: 0
在测试2中,即使SimpleClass对象超出范围,垃圾回收也不会调用其析构函数,直到从主函数调用垃圾回收为止。这是有好的原因吗?我猜测这是因为异步方法本身仍然“存活”,直到所有相关的异步方法都完成,因此它的变量也因此保持存活状态。
问题 - 本地对象在异步调用期间是否会被收集?
1.如果是,如何证明?
2.如果不是,我担心使用异步等待模式可能会导致内存不足异常,尤其是针对很大的对象。
非常感谢任何答案/评论。