Aug 112015
 

If there is one place where developers feel least guilty about allocating large amounts of memory, it’s in the local variables of the method. After all, local variables are short lived and as the method execution is over, the call stack is wound down and the return value is popped. This frees all of them for garbage  collection. This developer assumption might hold true for most methods, but not for all.

Let’s consider a simple method first. It allocates a large  1 million element long integer array and  returns the length of the array.

    class Program
    {
        static void Main(string[] args)
        {
            var a = new Foo();
            Console.WriteLine(a.Bar());
        }
    }
    class Foo
    {
        public int Bar()
        {
            var arr = Enumerable.Repeat(1, 1000000).ToArray();
            return arr.Length;
        }
    }

Let’s compile the program and open it in WinDbg. The commands for doing that are

  • .symfix (fixes the symbol path)
  • sxe ld:clrjit.dll (telling the code to break when clrjit is loaded)
  • g (continuing execution till the clrdll is loaded)
  • .loadby sos clr (This loads the SOS managed debugging extension)
  • !bpmd (Break when Program.main is executed)

Looking at the IL code of the Foo.Bar method, it’s pretty straightforward. A 1 million element array is created and then the ldloc.0 instruction loads the local variable on the stack. After the method returns, the local variable pointer no longer exists and the garbage collector is free to reclaim the memory for other objects.

Foo.Bar

This works quite well, but imagine a scenario where you might need access to the local variable even after the method execution is over.  One scenario is when the method returns  a Func delegate instead of an integer.

class Foo
{
    public Func<int> BarFunc()
    {
        var arr = Enumerable.Repeat(1, 1000000).ToArray();
        return () => arr.Length;
    }
}

Though this method will always return the same result as the previous method, the CLR cannot make that assumption and mark the integer array for collection. Because there is no guarantee that the returned delegate will be executed immediately or even just once, the CLR has to maintain a reference to the local variable even after the method execution is completed. The compiler resolves this dilemma by promoting the local variable on to the heap as a field of an autogenerated Type. Let’s see the IL generated when this new method is called.

Foo.BarFunc

 

This IL is considerably different. The newobj instruction creates an object of a new Type c__DisplayClass1 which we never created. That is the type which the compiler autogenerated and used for storing the local variable. Since the new type lives on the heap it’s lifetime is guaranteed till the return delegate’s reference is held on by the calling method.  We can verify this by examining the managed Heap

NewAutogeneratedType

 

…and the object of the autogenerated type shows our local variable now as a field.

DumpObject

 

If we modify our main method a bit and store the resulting delegate into a class level field , we can see that the GC maintains an explicit root to the object. In essence the object lives till the application execution is completed. This is unnecessary memory usage by the application.

class Program
{
    private static Func<int> classLevelVariable;

    static void Main(string[] args)
    {
        var a = new Foo();
        classLevelVariable = a.BarFunc();
        Console.ReadLine();
        classLevelVariable();
    }
}

Finding GCRoots for the object, we see that Garbage collector can never collect this object.

gcroots

This particular scenario might seem trivial, but in a LINQ-heavy production application it is very easy to lose track of the methods that are creating closures.  Awareness about the promotion of local variables can help prevent memory leaks and improve application performance.

 

Dec 262009
 

Events and Delegates are quite tied together in .NET, but there are differences in terms of usage. Events are implemented through delegates, but they are not quite interchangeable. The event keyword is an access modifier on the delegate which restricts its usage outside the class which it belongs to.

First the similarities. See the below code, which declares a delegate and creates objects for it with and without the event modifier. Then uses both of them, without any difference whatsoever.

    class Test
    {
        public delegate void MyDelegate(string _someRandomMessage);
 
        //One is a delegate while other is an event.
        public MyDelegate _mydelObj;
        public event MyDelegate _myeventObj;
 
        public Test(){
            _mydelObj = s =&gt; Console.WriteLine("Delegate Invoked: " + s);
            _myeventObj = s =&gt; Console.WriteLine("Event Invoked: " + s);
        }
 
        public void TestDelegates(){
            _mydelObj.Invoke("I am a delegate");
            _myeventObj.Invoke("I am an event");
        }
    }

The main difference starts when you access events outside of the class they were declared in. I created one more class SomeOtherClass (very unimaginative!!) and invoked the public delegate objects from this. When its a plain delegate object, there is no restriction on resetting the invocation list, adding to it, removing from it and also invoking the delegate itself. But try doing that to the event object, and you the compiler will throw the The event ‘EventsAndDelegates.Test._myeventObj’ can only appear on the left hand side of += or -= (except when used from within the type ‘EventsAndDelegates.Test’)

    class SomeOtherClass{
        Test _testObj = new Test();
        public SomeOtherClass()
        {
            _testObj._mydelObj += s =&gt; Console.WriteLine("Calling from Outside class: "+s);
            _testObj._myeventObj += s =&gt; Console.WriteLine("Calling Event from Outside class " + s);
            //The below code wont work. An event's invocation list cannot be accessed
            //from outside. It can be added to or removed from.
            _testObj._myeventObj = s =&gt; Console.WriteLine("Trying to reset the invocation list");
        }
 
        public void InvokeBaseDelegate(){
            _testObj._mydelObj("Delegate Invoked");
            //This will throw an error. An event cannot be triggered from anyother type
            //other than the one which it belongs to
            _testObj._myeventObj("Event Triggered");
        }
    }

This key difference is due to the way events and delegates are exposed outside the class which they are declared in. There is an extra IL declaration for an event object which declares two accessors add and remove for the event. Only these are accessible outside the class.

.event EventsAndDelegates.Test/MyDelegate _myeventObj
{
  .addon instance void EventsAndDelegates.Test::add__myeventObj(class EventsAndDelegates.Test/MyDelegate)
  .removeon instance void EventsAndDelegates.Test::remove__myeventObj(class EventsAndDelegates.Test/MyDelegate)
} // end of event Test::_myeventObj

When this is accessed in SomeOtherClass, the Delegate.Combine method used to add to the delegate object’s invocation list is not available for the event and the only alternatives are the add and remove accessors. Similarly the Invoke method is not available outside the class because of which the events cant be triggered.

.method public hidebysig specialname rtspecialname
        instance void  .ctor() cil managed
{
//Removed for Brevity
  IL_0035:  ldsfld     class EventsAndDelegates.Test/MyDelegate EventsAndDelegates.SomeOtherClass::'CS$&lt;&gt;9__CachedAnonymousMethodDelegate2'
  IL_003a:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  IL_003f:  castclass  EventsAndDelegates.Test/MyDelegate
//Removed for brevity
  IL_0067:  ldsfld     class EventsAndDelegates.Test/MyDelegate EventsAndDelegates.SomeOtherClass::'CS$&lt;&gt;9__CachedAnonymousMethodDelegate3'
//Notice the add_ accessor being called instead of the Delegate.Combine method
  IL_006c:  callvirt   instance void EventsAndDelegates.Test::add__myeventObj(class EventsAndDelegates.Test/MyDelegate)
  IL_0071:  ret
} // end of method SomeOtherClass::.ctor

It makes sense to restrict an event’s invocation list from an outside class. For example, if your application makes use of a public API which polls for the weather at a location and raises an event when there is going to be heavy snow. Someone in your team might have subsribed to the event to send out notifications to employees take caution while travelling. However, your need to subscribe to the event could be totally different. In that case if you use the = sign instead of the += while assigning a handler, the previous code for sending out notifications would not work. Needless to say, there would be a lot of pi**ed off employees baying for your blood the next day.

Apart from this, there is one more difference between delegates and events while declaring them in an interface. An interface cannot contain delegate objects, however they can contain events. If you try to do so, you would get an error Interfaces cannot contain fields.

Dec 232009
 

Anonymous methods are a convenient way of using delegates in .NET 2.0 and above. For e.g. if we use a delegate for a simple 2-3 line functionality, declaring a separate method and then passing the target to the delegate seems like overkill. Anonymous methods provide an easy way out, just declare the body of the method after the delegates declaration and you have no need to write a seperate method.

Anonymous methods are a compiler feature. Hence, from the CLR’s point of view there is nothing anonymous about it. The C# compiler generates a method with your delegate declaration and adds it to the invocation list for the delegate. Here is some sample code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 class TestClass
    {
        delegate void writeToScreen(string _inpar);
 
        public static void TestDelegates()
        {
            //1.1 way of doing things. Notice the seperate method.
            writeToScreen _delObj = new writeToScreen(new TestClass().NamedMethod);
 
            //2.0 way of calling it
            _delObj += delegate(string _inpar)
            {
                Console.WriteLine("Anonymous method says: "+_inpar);
            };
 
            //Lets invoke the delegate
            _delObj.Invoke("Hello World");
        }
 
        public void NamedMethod(string _inpar)
        {
            Console.WriteLine("Named method says: " + _inpar);
        }
    }

In the above code, the 1.1 way would have you scrambling to find the definition of the NamedMethod, but in second delegate its simpler and far more easier to understand. In the IL, the compiler generates a method for the anonymous block of code that you write.

1
2
3
4
5
6
7
.method private hidebysig static void  'b__0'(string _inpar) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
//removed for brevity
  IL_0010:  ret
}
// end of method TestClass::'b__0'

So far so good, but anonymous methods bring more questions to the table. How are variables in the outer scope treated? Can they be accessed? Yes they can and they have a fancy name as well – Captured variables. Here is some code for it.

1
2
3
4
5
6
7
8
9
10
11
12
13
            int _localVar = 0;
            _delObj += delegate(string _inpar)
            {
                _localVar++;
                Console.WriteLine("Anonymous method says: "+_inpar);
            };
 
            //Before invoking the delegate the value is 0
            Console.WriteLine("Local Variable : " + _localVar);
            //Lets invoke the delegate
            _delObj.Invoke("Hello World");
            //After invoking, the value becomes 1
            Console.WriteLine("Local Variable : " + _localVar);

Behind the screens the compilers does a lot of work to get captured variables work. In the MSIL, there is no sign of the local variable, instead a reference variable is created which has the local integer as a member and all modifications are done to it.

The IL shows the int declaration has disappeared in the TestDelegates method. Instead an instance of the class c_Displayclass1 is created and used. The local variable is a member of this instance class and this is the variable that is incremented both in the TestDelegates method as well as the invocation giving the illusion of usage of the local variable in the anonymous method body scope. For short declarations and one or two variables this feature would be quite useful, but a large number of captured variables would make the code difficult to understand, hence compromising the biggest advantage of anonymous methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.method public hidebysig static void  TestDelegates() cil managed
{
   .locals init ([0] class AnonyMeth.TestClass/writeToScreen _delObj,
           [1] class AnonyMeth.TestClass/'&lt;&gt;c__DisplayClass1' 'CS$&lt;&gt;8__locals2')
  IL_0000:  newobj     instance void AnonyMeth.TestClass/'&lt;&gt;c__DisplayClass1'::.ctor()
  IL_0005:  stloc.1
 //removed for brevity
  IL_0019:  stfld      int32 AnonyMeth.TestClass/'&lt;&gt;c__DisplayClass1'::_localVar
  IL_001e:  ldloc.0
  IL_001f:  ldloc.1
  IL_0020:  ldftn      instance void AnonyMeth.TestClass/'&lt;&gt;c__DisplayClass1'::'b__0'(string)
  IL_0026:  newobj     instance void AnonyMeth.TestClass/writeToScreen::.ctor(object,
                                                                              native int)
  IL_002b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  IL_0030:  castclass  AnonyMeth.TestClass/writeToScreen
  IL_0035:  stloc.0
  IL_0036:  ldstr      "Local Variable : "
  IL_003b:  ldloc.1
  IL_003c:  ldfld      int32 AnonyMeth.TestClass/'&lt;&gt;c__DisplayClass1'::_localVar
  //The instance variable of c__displayclass1 is displayed here
  //removed for brevity
  IL_0075:  ret
} // end of method TestClass::TestDelegates