【问题标题】:Does not having a reference to a Task cause memory leaks?没有对 Task 的引用会导致内存泄漏吗?
【发布时间】:2015-03-26 06:40:35
【问题描述】:

考虑以下代码sn-p:

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

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

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

【问题讨论】:

  • 如果您的代码只是 Task.Delay(5000);,您也可以这么说 - “没有维护对返回的任务的引用” 我很确定不会t 泄漏。
  • 即使您愿意,我认为您实际上也不会在托管上下文中泄漏内存。当引用类型的实例没有引用它时,GC 最终会来收集它。
  • 虽然不是泄漏,但你的任务将是“未观察到的”,如果抛出任何异常,未观察到的任务会导致混乱。它只能由任务终结器检测到,该终结器在垃圾收集期间在不可控的时间运行。所以我仍然建议重写你的代码。

标签: c# .net memory-leaks task-parallel-library


【解决方案1】:

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

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

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

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

因此,如果您继续创建任务,无论您是否持有引用,都可能会发生泄漏。

在您的具体情况下,在前 5 秒内,内部 Timer 引用了 Promise Task,而后者又引用了 Delegate Task(尚未安排,也没有与它)。 5 秒后,Timer 完成了 Task.Delay 任务,该任务又安排了 DoSomething 延续,因此线程将引用它。

如果DoSomething 从未完成,则表示内存泄漏(非常小),如果完成,则没有。


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

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.
    }
}

【讨论】:

  • Task.Delay(-1) 也会导致与Task.Delay(int.MaxValue) 类似的内存泄漏(至少在.NET 4.6 上)
  • @marchewek 你认为这会泄露什么?哪个类的实例?
【解决方案2】:

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

线程池线程在空闲一段时间后被回收(我认为默认约为 45 秒)。

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

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

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

【讨论】:

  • 谢谢,很好的解释。不是来自线程池的长时间运行的任务呢?
  • 你的意思是 LongRunning 标志吗?如果是这样,这只是向调度程序提供了一个提示,它可能希望在非线程池线程上执行任务。即使它确实在非线程池线程上执行,它仍然是托管线程,并且在执行完成后将被清理。此外,Stephen Cleary 在本文中指出,在大多数情况下您可能不需要 LongRunning 标志,因为调度程序能够在 0.5 秒内适应任何长时间运行的任务:blog.stephencleary.com/2013/08/startnew-is-dangerous.html
猜你喜欢
  • 1970-01-01
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 2021-09-25
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多