Charl Botha, senior developer at Intervate, and I had some discussions around .NET memory issues, and how to measure the size of a CLR object in memory.
Here’s what Charl had to say:
“I have yet to discover how one can retrieve the size of an object in pure CLR, the method that I used was to take an object created in CLR, get the GCHandle, then get the size of the object in unmanaged memory. This I don’t think would help you.
There is software available that you can use, free, that can measure memory usage.
They are dead slow since they are virtual machines, like Java, but are able to take the measurements for you.
The CLR profiler I found to be the best product to help me find out which objects are not memory happy.
Here’s a list of things that are not very good to memory and will abuse you system.
Note, this infromation was composed nearly two years ago based on the 1.1 .Net framework, so I’m not 100% sure that they still affect the system as badly as they did last time.
1. Delegates, the typical += assigning to events, a lot of coders abuse this and every time a new object is created they create a new delegate and bind it to the object, bad.
There should only be one delegate created when the assembly is loaded and reused, the -= must then be used to unbind the object when the destructor is called or disposed. Very, very important:
- First create a new instance of the EventHandler delegate:
ElapsedEventHandler _elapsedEventHandler = ElapsedEventHanlder( onTimedEvent );
- Then assign the EventHandler delegate to the object’s event:
_timer.Elapsed += _elapsedEventHandler;
- When we’re finished with the source object (_timer in the example), unbind the original delegate instance:
_timer.Elapsed -= _elapsedEventHandler;
2. Secondly, fonts, brushes, and graphics in general. Each graphically driven object needs a handle, and handles are created when the object is created. But when the GC collects the object, it does not dispose of the handle, bad.
Once again, use the object that are already created by the application and not manually by your code.
3. Streams, any stream reader or writer must be closed and disposed, if a coder does not do this, the handle to the memory is kept open until the process is killed. Always, close stream, so search through your code and see where you open streams, from files, to XML, to serialization.
4. Threads, every time a thread is created in the virtual machine, certain memory is allocated to control this thread, in the real world, i.e. OS systems 5+ allocate 1mb of memory to manage a thread, create a lot of threads, use a lot of memory, whenever possible use thread managers and processing queues to run transactions rather than just creating a thread for every transaction.
5. Lastly, use the GC.WaitForPendingFinalizers after transactions, this will help with thread memory allocation not just application memory, remember, threads drive process, processes, drive jobs.”
Hope this discussion helps others, as it helped me with some commonly encountered .NET memory usage and development issues.