Quick and Dirty Memory Utility Methods
Posted: 2/27/2008 7:36:28 PM
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;
}
Tags: Tips and Tricks