I am a developer at Microsoft and work in the .NET Common Language Runtime (CLR) team. For the last 4 years I have been working on virtual machine technologies on a variety of form factors including desktops (Windows, Linux), tablets (Win8), gaming-consoles (Xbox 360), mobile devices (Windows Phone 7, Windows CE, Symbian).I have worked on various core pieces of the runtime including Garbage Collector, memory manager, platform abstraction layer, runtime-performance, etc.Before working on .NET I worked on Visual Studio Team Foundation Server, Visual Studio Team System, Adobe Framemaker, Adobe Acrobat, Texas Instrument's Code Composer Studio.
Someone contacted me over my blog about his managed application where the working set goes on increasing and ultimately leads to out of memory. In the email at one point he states that he is using .NET and hence there should surely be no leaks. I have also talked with other folks in the past where they think likewise.
However, this is not really true. To get to the bottom of this first we need to understand what the the GC does. Do read up http://blogs.msdn.com/abhinaba/archive/2009/01/20/back-to-basics-why-use-garbage-collection.aspx.
In summary GC keeps track of object usage and collects/removes those that are no longer referenced/used by any other objects. It ensures that it doesn’t leave dangling pointers. You can find how it does this at http://blogs.msdn.com/abhinaba/archive/2009/01/25/back-to-basic-series-on-dynamic-memory-management.aspx
However, there is some catch to the statement above. The GC can only remove objects that are not in use. Unfortunately it’s easy to get into a situation where your code can result in objects never being completely de-referenced.
Example 1: Event Handling (discussed in more detail here).
Consider the following code
EventSink sink = new EventSink(); EventSource src = new EventSource(); src.SomeEvent += sink.EventHandler; src.DoWork(); sink = null; // Force collection GC.Collect(); GC.WaitForPendingFinalizers();
In this example at the point where we are forcing a GC there is no reference to sink (explicitly via sink = null ), however, even then sink will not be collected. The reason is that sink is being used as an event handler and hence src is holding an reference to sink (so that it can callback into sink.EventHandler once the src.SomeEvent is fired) and stopping it from getting collected
Example 2: Mutating objects in collection (discussed here)
There can be even more involved cases. Around 2 years back I saw an issue where objects were being placed inside a Dictionary and later retrieved, used and discarded. Now retrieval was done using the object key. The flow was something like
Now the object was not immutable and in using the object in step 3 some fields of that object got modified and the same field was used for calculating the objects hash code (used in overloaded GetHashCode). This meant the Remove call in step 4 didn’t find the object and it remained inside the dictionary. Can you guess why changing a field of an object that is used in GetHashCode fails it from being retrieved from the dictionary? Check out http://blogs.msdn.com/abhinaba/archive/2007/10/18/mutable-objects-and-hashcode.aspx to know why this happens.
There are many more examples where this can happen.
So we can conclude that memory leaks is common in managed memory as well but it typically happens a bit differently where some references are not cleared as they should’ve been and the GC finds these objects referenced by others and does not collect them.
I am sure most people haven’t yet forgotten the Y2K problem. This year our team faced a mini Y2K, but don’t worry we anticipated it and all is well.
If you head over to NETCF Wikipedia page you’d notice the NETCF versions look as follows
<MajorVersion>.<MinorVersion>.<Year><DayofYear>.<Rev>
A sample version number is
3.5.7283.0
This represents .NETCF version 3.5 build on the 283rd day of 2007. I guess by now you can guess the problem. We use a single digit to represent the year. By that nomenclature, the version of the build churned out today would be 3.5.0001.0 which is lower than the one generated the year before and would fail to install.
These numbers are automatically generated by scripts on the server that churns out daily builds. The numbering system was invented a long time ago in early 2000 and no one bothered to fix it. We anticipated that it’s going to fail as we move into the new decade and have updated it to now have 2 digits for the year (and yes we know it will break again in the future, but hopefully that’s too far out to care right now).
Happy new Year.