Confessions of a code junkie
 
Wednesday, February 27, 2008

Quick and Dirty Memory Utility Methods
 
With my work, I regularly deal with obscene amounts. Of course, our users demand that this information loads instantly and without running out of memory. So optimizing our data storage objects for time and memory constraints is incredibly important. So far, I think I've done a pretty good job, because I can load 125 million data points in a shade over 3 seconds (using a database to store some of this data flew out the window a while ago). But getting to this point has been an interesting journey. Doing quick prototyping of prospective storage mechanisms has been incredibly important and I thought I'd share some of the utility methods/classes I've created to speed up the process.

The first piece of code is incredibly similar to my StopwatchWriter Class. Instead of starting and stopping a Stopwatch, we're asking the garbage collector how much total memory is being used (in bytes), before and after you instantiate an object (or a whole set of objects)

public class MemoryWriter : IDisposable
{
    long _startMem;
    string _text;

    public MemoryWriter(string text)
    {
        _text = text + " - ";
        _startMem = GC.GetTotalMemory(true);
    }

    public void Dispose()
    {
        Console.WriteLine("mem: " + _text + (GC.GetTotalMemory(true) - 
_startMem).ToString()); } } Usage looks like: using (new MemoryWriter("CrazyBigObject")) { CrazyBigObject myCrazyBigObject = LoadCrazyBigObject(42); }

This is the most accurate way of figuring out how much memory a specific object is taking up in memory. But there is a limitation to this method if you're using it in a multi-threaded application, such as a Win Form because another running thread could dereference objects on the heap after you've instantiated your MemoryWriter , but before its been disposed. I prefer to only use this class in small throw away console applications, to make my results as accurate as possible.

Sometimes though, you'll find yourself looking at an object in someone else's code and you want to easily find out how much memory its taking up, but the object in question is "built" over several methods along with several other objects that you don't care about. Which makes it impossible to use theMemoryWriter Class. You can use a memory profiling application to do this, but I've found that they are notoriously hard to pick up and use. And due to the nature of how they work, they take ages to work because they have to take a snapshot before and after the code you care about (i.e. copy your 700MB object heap twice and then "diff" the two heaps). So I've wrote two small methods that serialize the object to a stream and then return the length of the stream. Note: This is approximately how much data the object is holding in memory. It may besignificantly less than how much space it takes up on the heap. Case in point: Dictionary<K, V> takes up much more space in memory than it does when serialized.That's because it only serialized the key value pairs and it re-hydrates the dictionary on deserialization. Nor will it reflect the size of any properties on your object that are marked with the NonSerialized attribute. So it is far from perfect, but it helps give you an idea with out the pain of using a memory profiler . You can use these methods by setting a break point and calling them in your watch window (I prefer doing that over the immediate window).

/// <summary>
/// Returns a rough approximation of the size of an object 
/// (including ALL objects in/directly referenced by the object)
/// </summary> public static long ApproxSize(object obj) { BinaryFormatter formatter = new BinaryFormatter(); long length; using (MemoryStream stream = new MemoryStream()) { formatter.Serialize(stream, obj); stream.Seek(0, SeekOrigin.Begin); length = stream.Length; } return length; }

Sometimes though, your object is very large and serializing the object in memory will cause an OutOfMemory Exception. So use the below method instead, it serializes the object to disk and gives you the size of the file (and deletes the file after its done).

/// <summary>
/// Returns the approximate size of very large objects
/// (time intensive) in bytes.
/// </summary>
public static long ApproxSizeLarge(object obj) { FileInfo info = new FileInfo(@"c:\approxSizeTemp"); BinaryFormatter formatter = new BinaryFormatter(); using (StreamWriter writer = new StreamWriter(@"c:\approxSizeTemp")) { formatter.Serialize(writer.BaseStream, obj); } long length = info.Length; info.Delete(); return length; }


Wednesday, February 27, 2008 - 1:36 PM CST - Permalink kick it on DotNetKicks.com   Comments [3] -
Tags: Tips and Tricks


Wednesday, February 27, 2008 2:41:52 PM (Central Standard Time, UTC-06:00)
Interesting strategy, I assume you are curious about the size of reference instances, not value types where you could use the sizeof operator. That being said, using either the managed profiler or the sos.dll are probably the most accurate. Things like cycles from the garbage collectors generation levels aren't taken into account in the difference.
Wednesday, February 27, 2008 3:29:38 PM (Central Standard Time, UTC-06:00)
@Nick,

Yeah, actually I was a really big fan of the Son of Strike (sos.dll) for a long time, but I stopped needing to use it and I found it hard to pick up and use again. You would think such a powerful tool would be better documented on the "tubes" but it really isn't. That and when you've got an app that is using 1000MB of memory sos will start spitting out a lot of data.

I've used the CLR Profiler in the past, but for whatever reason whenever I try to profile my application, I end up getting a null reference exception. Thanks for reminding me about it though, hopefully I'll be able to use it on homebrew projects.
Wednesday, February 27, 2008 3:38:59 PM (Central Standard Time, UTC-06:00)
Bah! Apparently Microsoft has some bad links because the Downloads page said that I was downloading the CLRProfiler v2.0, but it actually gives you the v1.1 version. That's why I've been getting the null reference exception. If anyone is interested, the CLR Profiler is a pretty awesome tool, and the proper link is:

http://www.microsoft.com/downloads/details.aspx?familyid=A362781C-3870-43BE-8926-862B40AA0CD0&displaylang=en
Comments are closed.
 


All Content © 2009, Jon von Gillern
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.


 


Tags

Archive

Blogroll