Question

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

Was it helpful?

Solution 2

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

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top