My favourite author Simon Singh is a wiz at analogies. In his book The big bang he explains concepts like the doppler effect and the theory of relativity using analogies with frogs and trains that makes it not only easy to understand but you will remember them forever because of the picture they paint in your head.
The other day at work I heard one of my colleagues explaining memory usage and why you get out of memory exceptions to one of his customers using a restaurant analogy. I've talked about OOMs and memory management in an earlier post but I found the analogy so amusing that I thought I'd share it (and yes, before you say it, i do admit i might have stretched the analogy a little too far:), and that it doesn't hold a candle to Simon Singhs analogies, but then again he sells books and i just rant in a blog...).
Disclaimer: In order to not get too longwinded I will simplify a lot of things, and say that for example the GC allocates 64 MB segments even though this differs between different framework versions and the size of the objects you allocate (read large object heap). Some other details are also dependent on configuration settings (i.e. using the /3GB switch etc.) but I will exclude such details from the analogy.
Analogy Part 1 - General memory usage
If you have read my earlier posts you will know that a process on a 32-bit system can typically address 2 GB of virtual address space. This is the memory that you have to work with, independently of how much RAM you have. More RAM is good for performance since you page less with more RAM, but it doesn't do anything to expand the 2 GB address space.
Picture this 2 GB address space as being the floor space of a restaurant.
When you allocate an object (whether it is .net or non-.net) you typically follow a two step process. You reserve the memory and then you commit space inside your reservation.
The reservation is equal to reserving a table at the restaurant. And just like in a restaurant, depending on the memory manager you use (we will get to that later) you will reserve the memory in chunks. Let's say for example you are a party of 3. It is not likely that there will be a table for 3 in the restaurant, but rather you would get a table for 4 out of which you use 3 seats and waste one seat.
In memory terms the space for the table you have reserved is called reserved memory (virtual bytes), and the actual space you use (for the 3 seats) is comitted memory (private bytes). The floor space that is not yet reserved is free memory.
On a pretty good restaurant night your restaurant/memory might look something like this where the blue areas are reserved space, red means committed space and white is free space.
Now, if someone calls in to make a reservation for 3 they will get the answer that the restaurant is full, since the only way to seat 3 people together is to seat them on a 4 seat table. Even though you could fit in two 2-seat tables that wouldn't be good since they all want to sit together.
Similarily when you make memory allocations you won't split a memory reservation out into different locations, it has to be allocated in one chunk or not at all. So the memory result in this case would be "out of memory", even though there is plenty of space left.
An observant person might also note that if we put the tables closer together so that they are completely side-by-side you could easily fit in a new table of 4, but reserved memory areas much like tables at some restaurants can't be moved.
When we talk about memory fragmentation we either talk about the free but unusable (because it is not large enough to fit a new table) we have, or how much of our reserved memory we are not using (difference between virtual bytes and private bytes).
Analogy Part 2 - The .NET GC
Most of the time when you create objects in an application, whether it is .NET or not you use some kind of memory manager (NTHeap, C++ Heap, GC etc.), and in the restaurant case you can think of the memory manager as an hostess that reserves seats for you and ushers you to the location where you are to be seated. For example if you call malloc you don't have to provide an address where you want your allocation to lie, instead you say that you want memory of a certain size and malloc returns, ok, you will be seated at table 1 in the "C++ heap" area.
The .NET GC takes this one step further and pre-reserves a large table for anyone who might want to use .NET objects in the process (let's say a 64 seat table). And when anyone creates a .NET object, the GC ushers them to the next available seat on that table. Once in a while the usher will walk around the table to check if someone is done eating and ask them to leave, and then scoots the rest of the people down the table. Some people might be waiting on other people to finish up before they can leave (references), so they get to stay too. And some people may be really annoying and say, dude, i got a window seat, i am sooo not moving (pinned objects) which means that the rest of the people can't be scooted down towards the end of the table either.
Any empty seats between people are referred to as .NET memory fragmentation.
Once the 64 seat table is filled up the GC needs to reserve a new 64 seat table if it needs to accomodate newcommers, and if it can't you will get an out of memory exception.
But, how does it really look
Ok, enough with the analogy, here is what memory looks like in a real ASP.NET application
Again, the red parts are committed memory, the blue parts are reserved memory that is not committed and the white space is free space.
The dots you see towards the end of the memory space are probably dlls, and although just like in the restaurant scenario there is a lot of white space, it is likely that none of the gaps between the small red dots are large enough to house a 64 MB segment and thus the next time we fill up a GC segment and need a new one to accommodate a new object, we will get an out of memory exception.
The reason these small red dots (dlls) are spaced out like this is because they are loading at the prefered base addresses for those particular dlls. You can't really do much about that type of fragmentation since it is hard to know in advance what a "good" prefered base address would be, but what you can do something about is finding out where the memory you are actually using is going.
A comment on performance counters and how not to use taskmanager
Throughout the analogy I talked about private bytes and virtual bytes and these are the two most important performance counters to look at when defining memory usage or memory leaks.
There is another counter called working set which simplified consists of how much memory is in the memory pages that are currently or was recently touched by threads in the process or approximately, how much of the memory that is used by the process is currently in RAM. The working set counter might be interesting if you have issues with too much paging and many processes on the same box competing about the RAM, but in order to determine how much memory you are using (reserved or committed) it offers little or no help.
If you want to see this in action, you can create a winforms application and allocate a bunch of objects and see the workingset go up, and then if you minimize the app, the working set drops. This doesn't by any means mean that you have just released all this memory. It just means that you are looking at a counter that is totally irrelevant for determining how much stuff you store in memory :) Yet... this is the counter that people most often look at to determine memory usage...
I know that by now you are probably thinking "yeah right", you haven't even heard of this counter before, why would I say that this is the counter most people look at??? The answer is, because most people use task manager to look at memory usage of a process, and specifically look at the Memory Usage column. Surprise surprise:) what this actually shows you is the working set of the process...
If you want to see private bytes which is a far more interesting counter, you sould look at the column in task manager that is labeled Virtual Memory Size (yeah, that's really intuitive:)), or better yet, look in performance monitor at process\private bytes and process\virtual bytes, there is no reason not to if your intent is to investigate high memory usage or a memory leak.
So tonight, go out, grab a bite to eat and see memory management in action:) I bet you will probably find a lot more similarities than the ones me and my pal came up with...
thanks for your help.
In fact that I try to say to my customer for the whole week :P one more evidence helped me to bring him to reality :) now we're going towards a realistic solution :P
Veremos de firma simple y didáctica como funciona la asignación y administración de memoria de Windows y .net.
Tess I think your great (I had a fortune of getting help from you on number of support cases and you are definetly one of the best MS support engineers I worked with).
Does microsoft (perhaps internally) has a utility that can analyze some of this stuff automaticly? If you have the dump then I am sure alot of these problems can be diagnosed automaticly. Or am I over simplifying here?
First off, thank you very much:) that makes me blush:)
We are working on scripts for automating some parts of .net debugging. You may be familiar with a tool for automation of native debugging called debug diagnostics 1.1. In essence we are looking into what we can do as far as writing scripts for that for .net debugging, but there are some parts, especially memory wise that are a bit hard. For example figuring out which objects are interesting to look at based on the !dumpheap -stat output and then determining what roots are interesting since the job is based on a lot of trial and error.
It is fairly easy to write scripts on your own for debug diag (vbscript files) and a good start is to write one that prints out output from !dumpheap -stat, !dumpheap -min 85000, !eeheap -gc, ~* e !clrstack, !threads, !dumpdomain, !syncblk and ~* kb
If you just create that "simple" script it takes you a long way into seeing what is going on in most cases, and writing the script is as easy as writing
Manager.Write g_Debugger.Execute("!dumpheap -stat")
in one of the scripts in the debugdiag scripts folder (of course you have to make sure to load sos first).
Before we have anything that is usable in a more general sense it might be a while, but as soon as we do, I will post it on the blog.
I am trying to figure out how much RAM our .NET app. is actually using and used your recipe above monitoring with Private and Virtual bytes with PerfMon. This seems to make sense, BUT:
1) Why is page file constantly growing, when their is sufficient with RAM left? It grows with app. the same size as RAM being used. Has it something to do with Virtual memory always requires disk??? (I am blank :-)))
2) Why is a high RAM usage "maintained" even after data has been discarded? (first I complete heavy search, then complete less heavy search...dataset is nulled each time) I would expect it to go down immediately.
The hierarchy in flat memory: Heap and Stack This section discusses Heap, related heap corruption/memory
I was working through the High CPU Lab Review which is basically caused by high CPU in GC. To understand
Tess, that's a wonderful way to explain memory management. I had a query about the following statement though.
"When you allocate an object (whether it is .net or non-.net) you typically follow a two step process. You reserve the memory and then you commit space inside your reservation."
In context of .Net memory management, when is that we reserve the memory and when is it commited. Can you please elaborate?
memory on the .net gc heaps are reserved in segments so everytime you fill up a segment and need more space you would reserve a segment which might be for example 16 MB, 32 MB, 64 MB or even larger depending on framework version, GC mode, OS architecture etc.
Either way, you reserve the 64 MB for example but you wouldn't commit memory inside them until you actually allocate a new .net object. Now if that .net object was just 2k then you would have ~64 MB reserved and 2 k committed... if you allocate another 3k object you would then have 64 mb reserved and 5k committed etc. etc.
Thanks for writing this.
For everyone that doesn't like Task Manager, try Process Explorer, it's free and it shows all the memory info you want and has tons of features.
I recommend the full suite or atleast looking at each app in the suite and choosing what you like.
Its an excellent article on memory management. Is there any way to identify the memory being consumed by DLL's used.
A MOSS application has many third party webparts and controls and http handlers. How can we isolate which webpart or which control is taking more memory.
Because I have observed that application stops serving some of the HTTP handlers until we do a app-pool recycle. Then immediately that handler works and serves the requests and again stops after some time...
There isnt really a way to record memory allocated by dll (assuming you mean memory allocated by the code in that particular assembly)
You would have to do a normal memory investigation, check out some of the labs around memory (on the right hand bar) to see how you can do memory investigations...
I stumbled upon your article doing a Google search for why Task Manager didn't reflect the correct memory usage. When adding the memory usage for 6 processes it would be up to 8GB and the server only has 3GB! Your article really cleared things up for me by explaining the difference between private bytes & virtual bytes. I'm still not sure how those processes can amount to 8GB but thanks for writing this article!
while you only have 3 GB of RAM that doesnt mean that the combined memory usage for your processes can't be more. Any memory that doesn't fit in RAM will be swapped out to disk in page files
Linki, które posłużyły mi przy tworzeniu prezentacji, z których czerpałem wiedzę, nakładałem ją na to