【问题标题】:Why Garbage Collector doesn't collect Tasks objects为什么垃圾收集器不收集任务对象
【发布时间】:2016-07-22 16:13:01
【问题描述】:

尤其是当没有活动线程引用它时。

我认为 GC 会考虑所有 .net 线程来查找引用...它是否也检查其他地方的引用?

编辑:例如让我们假设我们在一个控制台应用程序中,main 调用一个创建本地 task1 的方法,然后应用 task1.ContinueWith(task2) 并返回到 main,main 执行 console.readline()。

此时可能是task1已经完成,task2还没有启动GC可以启动,没有线程引用task2。为什么 task2 没有被 GC 处理?

EDIT2:我说“任务”时可能没有使用正确的词

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        static void Launch()
        {
            var task1 = Task.Run(() => Thread.Sleep(60000))
            task1.ContinueWith(() =>  WriteToFile("Hi"));
        }

        static void Main(string[] args)
        {
            Launch();
            //At this point if a GC occurs which thread or static file has a reference to "()=>WriteTofile("Hi")" ?
            Console.ReadLine();
        }

有一个主线程在等待控制台,一个线程(可能来自线程池)运行睡眠。在 Sleep 完成之后,WriteToFile 线程开始之前,可能会发生 GC,不是吗?

【问题讨论】:

  • 你能用一些实际的代码来说明这个问题吗?假设 task1task2Tasks,task1.ContinueWith(task2) 不会编译。
  • 您认为“仍未开始”是有效场景的假设是不正确的,task2 在 task1 完成后立即开始。如果线程池运行缓慢,那么它仍然无关紧要,因为线程池队列有对它的引用。注意 task 参数是如何在 this code 中传递的。
  • 我没有任何代码。这只是理论上的。关于线程池队列,并不是所有的任务都由线程池执行,不是吗?线程池队列何时引用task2?

标签: .net garbage-collection task-parallel-library


【解决方案1】:

task1 的引用被保留by the default task scheduler,(默认任务调度程序is static)。

is kept alive by task1 的延续直到它被移交给它被分配了任务调度程序(TaskScheduler.Current 在默认情况下创建时)。

(请注意,这些可能不是唯一可能的根源,只是我通过源代码快速找到的那些)

【讨论】:

  • Ups... 我忘了考虑 GC 根的静态引用 :(.
  • 但是task1可以入队到defaultTask Scheduler,可以更改默认taskscheduler,继续可以入队到新的task scheduler,然后再更改默认task scheduler
  • task1 引用了它的所有延续。即使任务继续设置的调度程序“已死”,您仍然拥有来自 task1 的引用。
  • 直到 task1 完成。然后延续只被“死”调度器引用。可能“死”调度器有一个静态字段指向包含延续的队列。
【解决方案2】:

我怀疑您的部分误解是您编写的代码没有理由运行垃圾收集。你说“就在睡眠之后......可能会发生 GC [?]”,但实际上,不:垃圾收集器不会运行,因为没有发生任何事情。

当您分配内存时,会在 .NET 中自动进行垃圾收集。那就是:当您评估一个表达式并将该值写入内存时。垃圾回收仅适用于堆,因此通常会在您通过调用构造函数创建类的实例时触发 GC。

即使那样,垃圾回收也只会在第 0 代没有足够的空闲内存来存储新创建的对象时才会发生(即使那样,假设对象不是太大以至于它直接进入大对象堆)。

考虑到这一点,您发布的代码没有分配足够的内存来触发 GC,因此我不希望在程序终止之前收集在那里创建的任何对象。

【讨论】:

  • 我提供的代码只是一个骨架。假设一开始我安排另一个线程不断分配新对象......或者在调用 Console.ReadLIne 之前我睡了一段时间并运行 GC.Colect
  • 好吧,如果您发布更具体的代码,我们可以给出更具体的答案。如果您手动调用 GC,那么某处必须有一个根(如 Scott 的回答中所建议的那样) - 但您没有在问题中解释这一点。
  • 感谢您的回复。我的英语很差,经常问不出正确的问题。我一直在寻找这种特殊情况的根源。
猜你喜欢
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 2010-12-19
  • 2014-08-08
相关资源
最近更新 更多