2

especially when no live Thread reference it.

I thought GC goes thought all .net threads to find references... Does it check references in other places too?

EDIT: FOr instance let's imagine we are in a console app, the main calls a method that creates a local task1, then applies a task1.ContinueWith(task2) and returns to main, main do console.readline().

At this point it could be that task1 has finished, task2 still has not started a GC could start and no thread has a reference to task2. Why task2 doesn't get GC'ed?

EDIT2: Probably I'm not using the right words when saying "task"

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();
        }

There is the main thread waiting for the console, one thread (maybe from the threadpool) running the Sleep. Just after the Sleep is done, and before the WriteToFile thread start, a GC could happen, isn't it?

Eduard
  • 664
  • 2
  • 14
  • 26
  • Could you show the issue using some actual code? Assuming `task1` and `task2` are `Task`s, `task1.ContinueWith(task2)` wouldn't compile. – svick Jul 22 '16 at 17:59
  • Your assumption that "still has not started" is a valid scenario is just not correct, task2 gets started *immediately* after task1 completes. If the threadpool is slow to get it going then it still doesn't matter since the threadpool queue has a reference to it. Note how the *task* argument gets passed in [this code](http://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/ThreadPoolTaskScheduler.cs,33cd274e06874569). – Hans Passant Jul 22 '16 at 18:56
  • I don't have any code. It is just theorical. Regarding the threadpool queue, Not all tasks are executed by the threadpool,isn't it? At which point task2 gets referenced by threadpool queue? – Eduard Jul 22 '16 at 20:55

2 Answers2

7

The reference to task1 is being held by the default task scheduler, (The default task scheduler is static).

The continuation is kept alive by task1 until it is handed off to it's assigned the task scheduler (TaskScheduler.Current at the time of creation by default).

(Note, these are likely not the only possible roots, just the ones I quickly found looking through the source)

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Ups... I forgot to consider static references for GC roots :(. – Eduard Jul 22 '16 at 22:06
  • But task1 can be enqueued to defaultTask Scheduler, the default taskscheduler could be changed, the continuation could be enqueued to the new task scheduler, and then again change the default task scheduler – Eduard Jul 22 '16 at 22:09
  • task1 has a reference to all of it's continuations. Even if the scheduler the task continuation was set to was "dead" you still have the reference from `task1`. – Scott Chamberlain Jul 22 '16 at 22:28
  • Until task1 finish. Then the continuation is only referenced by a "dead" scheduler.Probably the "dead" scheduler has a static field pointing to a queue that contains the continuation. – Eduard Jul 22 '16 at 22:42
1

I suspect that part of your misunderstanding is that there is no reason that the code you've written would run a garbage collection. You say "just after the Sleep ... a GC could happen[?]" but actually, no: the garbage collector will not run simply because nothing is happening.

Automatic garbage collection in .NET happens when you allocate memory. That is: when you evaluate an expression and that value is written to memory. Garbage collection applies only to the heap, so typically a GC is triggered when you create an instance of a class by calling a constructor.

Even then, garbage collection will only happen if there is not enough free memory in generation 0 to store the newly-created object (and even then, assuming the object isn't so large that it goes straight into the Large Object Heap).

With that in mind, the code you've posted doesn't allocate enough memory to ever trigger a GC, and so I'd not expect any of the objects created there to be collected until the program terminates.

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
  • The code I provided is just an skeleton. Let's say at the very begining I scehdule another thread that is constinuosly allocating new objects... Or before calling Console.ReadLIne I sleep some time and run a GC.Colect – Eduard Jul 22 '16 at 22:27
  • Well, if you post more specific code, we could give a more specific answer. If you're manually invoking a GC then there must be a root somewhere (as suggested in Scott's answer) - but you didn't explain that in your question. – Dan Puzey Jul 22 '16 at 22:29
  • Thanks for the response. My english is poor and often I'm not able to ask the proper question. I was looking for the roots in this particular case. – Eduard Jul 22 '16 at 22:37