如果我创建/订阅并启动它,System.Timers.Timer
实例在我的应用程序中创建了哪些引用?
以下是我所知道的参考资料:
Elapsed
事件我假设在我们的Start
计时器时必须创建一些额外的引用。但他们是什么?
以下是我尝试过的一段代码:
class Program
{
static void Main(string[] args)
{
var timers = new List<Timer>();
Console.WriteLine("Timers to create/start and subscribe: ");
// step 1
var count = 1000000;
for (int i = 0; i < count; i++)
{
var timer = new Timer(100000);
timers.Add(timer);
// step 2
timer.Elapsed += Callback;
timer.Start();
}
// step 3: 7mb -> 240mb
Console.WriteLine("All timers subscribed and started");
Task.Delay(TimeSpan.FromSeconds(15)).Wait();
// step 4
timers.ForEach(t => t.Elapsed -= Callback);
Console.WriteLine("All timers unsubscribed");
// step 5: uncomment next two lines to have timers GC collected.
// timers.ForEach(t => t.Stop());
// Console.WriteLine("All timers stopped");
// step 6
timers.Clear();
Console.WriteLine("Collection cleared");
// step 7
do
{
Console.WriteLine("Press Enter to GC.Collect");
Console.ReadLine();
GC.Collect();
Console.WriteLine("GC Collected");
}
while (true);
}
private static void Callback(object sender, ElapsedEventArgs e)
{
Console.WriteLine("callback");
}
}
这是我在这里做的事情:
Timer
个实例并将其存储到List Elapsed
事件静态方法参考,然后调用Start
。Elapsed
事件,从而杀死隐式事件订阅引用。 如果Step 5
被执行(计时器被停止),那么在GC.Collect
步骤我可以看到我的应用程序占用的内存从240mb减少到大约20mb,这意味着Timers被正确回收。
如果省略Step 5
,则在GC.Collect
步骤内存使用率保持在240mb级别,无论我等待多长时间并强制GC收集。这实际上意味着计时器实例保留在内存中并且无法收集。
根据我所知道GC的工作方式,如果无法从根目录访问我的计时器,则应该从内存中清除它们(意味着没有引用)。
我是否可以在没有明确调用timer.Stop()
的情况下消除我的计时器的任何其他引用?
在不调用Stop
方法时是否还有其他方法可以防止内存泄漏?