1

I've readed about this, but I forgot, where I saw an example. So it looks like this

public class Program
{
    private static void Main()
    {
        new SomeClass(10).Foo();
    }
}

public class SomeClass
{
    public int I;

    public SomeClass(int input)
    {
        I = input;
        Console.WriteLine("I = {0}", I);
    }

    ~SomeClass()
    {
        Console.WriteLine("deleted");
    }

    public void Foo()
    {
        Thread.Sleep(2000);
        Console.WriteLine("Foo");
    }
}

so output should be:

I = 10
deleted
Foo

why? due to optimizer. It sees that method doesn't use any field so it can destroy an object before the method is called. So why it doesn't do it?

I'l post an example if i found it.


so i found the source. Pro .NET Performance: Sasha Goldshtein , Dima Zurbalev , Ido Flatow

Another problem has to do with the asynchronous nature of finalization which occurs in a dedicated thread. A finalizer might attempt to acquire a lock that is held by the application code, and the application might be waiting for finalization to complete by calling GC.WaitForPendingFinalizers(). The only way to resolve this issue is to acquire the lock with a timeout and fail gracefully if it can’t be acquired. Yet another scenario is caused by the garbage collector’s eagerness to reclaim memory as soon as possible. Consider the following code which represents a naïve implementation of a File class with a finalizer that closes the file handle:

class File3
{
    Handle handle;
    public File3(string filename)
    {
        handle = new Handle(filename);
    }
    public byte[] Read(int bytes)
    {
        return Util.InternalRead(handle, bytes);
    }
    ~File3()
    {
        handle.Close();
    }
}

class Program
{
    static void Main()
    {
        File3 file = new File3("File.txt");
        byte[] data = file.Read(100);
        Console.WriteLine(Encoding.ASCII.GetString(data));
    }
}

This innocent piece of code can break in a very nasty manner. The Read method can take a long time to complete, and it only uses the handle contained within the object, and not the object itself. The rules for determining when a local variable is considered an active root dictate that the local variable held by the client is no longer relevant after the call to Read has been dispatched. Therefore, the object is considered eligible for garbage collection and its finalizer might execute before the Read method returns! If this happens, we might be closing the handle while it is being used, or just before it is used.

but i can't reproduce this behaviour

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151

3 Answers3

2
public void Foo()
{
    Thread.Sleep(1000);
    Console.WriteLine("Foo");
}

Methods that don't use any instance member of a class should be declared static. Which has several advantages, it is for one very helpful to a reader of the code. It unambiguously states that a method doesn't mutate the object state.

And for another has the great advantage that you'll now understand why there's no discrepancy in seeing the method running after the object got finalized. The GC just doesn't have any reason to keep this alive, there are no references left to the object when Foo() starts executing. So no trouble at all getting it collected and finalized.

You'll find more background info on how the jitter reports object references to the garbage collector in this answer.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I can work with fields, and the output should be the same, due to CLR standard. Simple WriteLine is used just for example – Alex Zhukovskiy Dec 01 '13 at 19:52
  • It will of course not be the same. Due to "CLR standard", a method cannot ever get to access to a field of a finalized object. Clearly that would disastrous. – Hans Passant Dec 01 '13 at 20:03
  • But it still be written that `The rules for determining when a local variable is considered an active root dictate that the local variable held by the client is no longer relevant after the call to Read has been dispatched. Therefore, the object is considered eligible for garbage collection and its finalizer might execute before the Read method returns! If this happens, we might be closing the handle while it is being used, or just before it is used.` So it's wrong? – Alex Zhukovskiy Dec 01 '13 at 20:16
  • No, that's not wrong. It is exactly what you are seeing, the object is gc-ed while Foo() is executing. The `new SomeClass()` reference is no longer relevant. You don't store it in a local variable but that doesn't matter. – Hans Passant Dec 01 '13 at 20:34
  • I'm trying to reproduce it, using a field, but have no success. Field now is `Stream`, and method call `Stream.Read`, so now object is deleted `after` Foo method finished, irrespective of how long does it work. – Alex Zhukovskiy Dec 02 '13 at 03:50
  • @Alex - Hans is right. If i write this line in `Foo` method - `Console.WriteLine("I = {0}", I);` i.e. access class field in method, object is not GC'ed before method execution finish. (Tested both in release and debug mode) – Rohit Vats Dec 02 '13 at 07:18
  • @RohitVats in the qoute we see that it can be collected by standard, not nececcary, but possible. – Alex Zhukovskiy Dec 02 '13 at 10:42
0

Anyway, i found the way to reproduce it, i just should read more attentive :) :

public class Program
{
    private static void Main()
    {
        new Thread(() =>
                   {
                       Thread.Sleep(100);
                       GC.Collect();
                   }).Start();
        new SomeClass(10).Foo();
    }
}

public class SomeClass
{
    public int I;

    public SomeClass(int input)
    {
        I = input;
        Console.WriteLine("I = {0}", I);
    }

    ~SomeClass()
    {
        Console.WriteLine("deleted");
    }

    public void Foo()
    {
        Thread.Sleep(1000);
        Console.WriteLine("Foo");
    }
}

so in this case destructor will be called before Foo method. enter image description here

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • Code still prints `Foo` before `deleted` since `Main` method is still executing so `SomeClass` instance never goes out of scope hence not eligible for GC collection. – Rohit Vats Dec 01 '13 at 07:29
  • Perhaps you compile in debug? – Alex Zhukovskiy Dec 01 '13 at 07:32
  • @RohitVats Yes, `Main` is still executing, but there is no need to keep the pointer. In Release mode the optimization allows to throw the pointer away and GC the object. If you sleep long enought the object has enought time to go throw finalization queue and execute finilizer. See my answer to see how to avoid such behavior. – Aleksei Poliakov Dec 01 '13 at 07:36
  • @Alex - Yeah i was compiling in `Debug` mode but once i compile in `Release` mode, it gives the output as you mentioned. Wasn't aware of Release mode optimization. +1 for you.. – Rohit Vats Dec 01 '13 at 07:42
  • @DarkWalker - Can you share some link about Release mode optimizations where it's explained in detail. Just for reference. – Rohit Vats Dec 01 '13 at 07:43
  • 1
    @RohitVats Sorry, I have never seen such article. And it is important to remember that optimization is an implementation detail and could change between compiler versions. This exact optimization in known and described in Clr via C# book (page 510, Part IV: Core Facilities, Chapter 21: The Managed Heap and Garbage collection) and various internet articles, like: http://www.codeproject.com/Articles/634451/Csharp-and-GC-KeepAlive – Aleksei Poliakov Dec 01 '13 at 08:02
  • You should mark your own reply as an answer. I think for the sake of completeness you need to mention that using [GC.KeepAlive](http://msdn.microsoft.com/en-us/library/system.gc.keepalive%28v=vs.110%29.aspx) 'solves' the problem if you had one. – Aleksei Poliakov Dec 01 '13 at 07:32
  • not rly, just a post about some little known optimization. I can mark my answer only in 2 days. – Alex Zhukovskiy Dec 01 '13 at 07:34
  • @RohitVats Your question really got into my head and I finally found a list of some sort in [this answer](http://stackoverflow.com/a/4045073/305092) – Aleksei Poliakov Dec 29 '13 at 09:16
  • Yeah i found that too and it's already made its way to my favourite questions. :) – Rohit Vats Dec 29 '13 at 09:20
0

The problem is because you're using threading in Foo. You tell the code to wait for 1 second, but you don't tell it to wait for the second to be up before executing everything else. Therefore the original thread executes the destructor before Foo finishes.

A better way of writing Foo would be something like this:

public void Foo()
{
  var mre = new ManualResetEvent(false);
    mre.WaitOne(1000);

  Console.WriteLine("Foo");
}

Using the ManualResetEvent will force the code to completely pause until, in this case, the timeout is hit. After which the code will continue.

sbrauen
  • 113
  • 6