没有对 Task 的引用会导致内存泄漏吗?

Does not having a reference to a Task cause memory leaks?

考虑以下代码片段:

public void Do()
{
   ....
   Task.Delay(5000).ContinueWith(t => DoSomething());
   ....
}

假设 Do 方法在 Delay 任务完成之前完成执行,并且 DoSomething 不可取消。

没有维护对 ContinueWith 方法返回的 Task 的引用这一事实是否会导致某种内存泄漏?

任务将在线程池线程上执行,任务完成后线程将返回池中以供其他任务使用。

线程池线程在闲置一段时间后被回收(我认为默认情况下大约为 45 秒)。

因此线程池持有对它的引用这一事实将阻止它被垃圾收集。

我想唯一需要注意的是主应用程序线程必须是 运行ning。例如,如果您 运行 在控制台应用程序中执行上述代码,执行将在任务之前完成,因此任务永远不会 运行.

简而言之,不会 - 该代码不会导致内存泄漏。

您可以通过创建未收集的新任务来泄漏(托管)内存吗?
是的。

保留(或不保留)对它的引用对收集它有任何影响吗?
通常*不会。

有两种类型的任务:承诺(异步)任务和委托(同步)任务。

  • Promise 任务(如 Task.Delay)通常不会被收集,因为某些东西会保留对它的引用,因此它可以在需要时更改其状态(在 Task.Delay 中它是内部 Timer 需要在延迟结束时完成 Task
  • 委派任务 () 被 运行 线程(和 TaskScheduler)引用。

因此,如果您继续创建任务,无论您是否持有参考,都可能会出现泄漏。

在您的特定情况下,在前 5 秒内,内部 Timer 引用 Promise Task,后者又引用委托 Task(尚未安排并且没有与之关联的线程)。 5 秒后,Timer 完成 Task.Delay 任务,该任务又安排 DoSomething 继续,因此线程将引用它。

如果 DoSomething 从未完成,则表示存在内存泄漏(非常小的一次),如果它完成了,则不会。


* 可以 创建仅由您引用和不再引用它们的任务(两种类型)它们可以被 GC 收集。所以虽然这样:

static void Main()
{
    while (true)
    {
        Task.Delay(int.MaxValue);
    }
}

将在几秒钟内产生 OutOfMemoryException,这可以 运行 永远:

static void Main()
{
    while (true)
    {
        new Task(() => Thread.Sleep(int.MaxValue)); // No thread as the task isn't started.
        Task.Delay(-1); // No Timer as the delay is infinite.
    }
}