9

I ran some code last Friday before leaving work, and I'm here on Monday, and it stopped with an OutOfMemoryException. This entire process I estimate required making tens of billions of calculations, so it wasn't some small task.

I don't even know how to begin fixing this.
Any pointers?

sooprise
  • 22,657
  • 67
  • 188
  • 276
  • Do you have any recommendations for one? – sooprise Sep 13 '10 at 12:50
  • possible duplicate of [Any decent C# profilers out there?](http://stackoverflow.com/questions/10644/any-decent-c-profilers-out-there) – Hans Passant Sep 13 '10 at 12:56
  • What are you doing with the results of the calculations as they are produced? – cjk Sep 13 '10 at 13:01
  • @Hans: not really -- he's asking for tips on how to begin approaching this problem. A memory profiler is a possible solution, but not the only one required to get started on debugging the memory leak. – Dave Sep 13 '10 at 13:01
  • Is this code you wrote? If not, talk to the vendor. – Hogan Sep 13 '10 at 13:02
  • Ok, one thing that might be the problem is a public static array that is given data every time an instance of the class it is in does a calculation. I'm testing it right now, but it might take a few hours before I get a meaningful result (this program is big). Any thoughts on what effect a static array like this will have on things like OutOfMemoryExceptions? Thanks – sooprise Sep 13 '10 at 13:51
  • @Soo sure, that static array will keep growing forever. The question is, do you need it to be class data instead of instance data? Are you trying to accumulate all of your calculations over time? What are you eventually going to do with the calculations? Can you make it so that it doesn't need to keep a full history of of the calculations? – Dave Sep 13 '10 at 15:01
  • @Dave, the array contains 500 data objects (each of these objects contain sums for one of 500 timesteps), and no more than that. Will it continue to grow in that case? – sooprise Sep 13 '10 at 15:55
  • If the array is capped at 500, I wouldn't expect its memory footprint to grow, as long as you're storing sums, and not objects that represent sums that can themselves hold onto memory. It's just a guess on my part. You should post code examples for your calculation and the class that holds the sums. Are the debugging hints helping you at all in this? – Dave Sep 14 '10 at 12:24

8 Answers8

9

Well, given the contet you provided, or the lack thereof, I can only provide some general ideas here: First of all, the obvious answer would be to take a look at the information contained in the exception itself, which should give you an idea of where in your code allocation failed.

Second, you employ memory profiling to get a better idea of what is going on in your application - I'm a user of dotTrace, but there might be free alternatives available.

Apart from this general adivce, you might want to include some more information in your question. What type of objects are you allocating, when are you allocating, are you using native resources etc.

Jim Brissom
  • 31,821
  • 4
  • 39
  • 33
9

This post from Eric Lippert may help: “Out Of Memory” Does Not Refer to Physical Memory

dtb
  • 213,145
  • 36
  • 401
  • 431
asawyer
  • 17,642
  • 8
  • 59
  • 87
  • The link above is dead and not even available via the Wayback Machine, [this mirror of the post](https://learn.microsoft.com/en-us/archive/blogs/ericlippert/out-of-memory-does-not-refer-to-physical-memory) is still up though. – xanth Jul 26 '22 at 23:30
7

The simplest, most straightforward solution would be this:

  1. Use procdump (included in Sysinternals Suite) to take a full memory dump of your process when it reaches some unreasonable size.
  2. Load the dump into WinDbg and also load the SOS debugging extension using .loadby sos clr
  3. Use the following command: !dumpheap -stat in order to see which type of objects takes up the most of memory.
  4. Dump the list of objects (of the leaking type) using !dumpheap -type <MY_TYPE>
  5. Choose a couple of instances, and issue the command: !gcroot <OBJ_ADDRESS>. The output should tell you which object still holds a reference to it, and why does that object isn't released.

In case you suspect the source of leak is from some native code, you could verify this by issuing the following command: !eeheap -gc. The output will tell you how much memory does the managed heap takes. If you're process's private working set size is substantially larger than the managed heap's size, then you've probably got a native leak on your hands (or, perhaps from some reason you've spawned a lot of threads, so you're running out of space due to the thread's stacks [you could check how many threads are spawned in your process by issuing this command: ~*, or: !threads to show only the managed ones]).

Liran
  • 1,596
  • 1
  • 13
  • 11
4

If you are allocating very large arrays repeatedly, you may be running into Large Object Heap Fragmentation. If this is the case, you may need to consider whether you can reuse work arrays to prevent the re-allocations and subsequent fragmentation. I definitely recommend running a memory profiler, however, as it's more likely that you have something lingering in memory past what you assumed was its expiration date.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • I'm using a static array so each instance of the class it is in can add to it. There are approximately 12m instances of this class in my program. If this static array is the reason why this error is showing up, how can I better manage the memory being used by this array? – sooprise Sep 13 '10 at 14:16
  • 1
    The array is static, as in a static member of the class? If that's the case, I wouldn't expect that to cause LOH fragmentation, as it's only a single array. – Dan Bryant Sep 13 '10 at 14:55
3

There are several things you can try.

First, do a rough pass with the Performance Monitor, built into Windows. Add a counter for Process -> Private bytes used, and adjust the scaling accordingly so you can get a quick glance at memory usage. Instead of waiting overnight, you'll be able to identify a leak a little sooner. Another counter you can try is found in .NET Memory, but since my performance monitor isn't currently letting me add the .NET counters (ARGH!), I can't tell you exactly what the sub-selection is.

Are you allocated memory via unmanaged code in your calculation function? If so, are you Disposing of the memory properly? Any code you write that allocates memory this way should implement IDisposable, and you should also call Dispose() on these classes to make sure they get cleaned up.

Are you calling your function recursively? You could be allocating tons of memory each time through your function, and while you might not blow the stack, you'll be using up the heap.

Is there any memory you can get rid of sooner? Even if you aren't using unmanaged code, you can still flag your managed code so that the garbage collector deletes it sooner and then it won't end up in the Gen2 heap. Implement IDisposable, and then call Dispose() on classes that you don't need to hang around anymore.

Are you using threads? You could be allocating memory in your thread, and if you don't properly handle errors by calling EndInvoke, you'll orphan memory in this way.

If all else fails, try a memory profiler like ANTS5 from RedGate. It found another bug in my code, where I was registering an event handler, but not unregistering it. As a result, my code hung onto all of the bits and pieces related to this event, which unfortunately was also hanging onto bits of memory. :)

Hope this helps. I've gone through all of this recently and these are the most helpful tips I can come up with right now.

Dave
  • 14,618
  • 13
  • 91
  • 145
2

I gather you are storing the results of your calculations in a container like a list or an array. Try writing your results to file rather than storing them in memory.

Kogitsune
  • 415
  • 4
  • 8
  • It's in a static array so each instance of the class it is in can add to it. There are approximately 12m instances of this class in my program. If this static array is the reason why this error is showing up, how can I better manage the memory being used by this array? – sooprise Sep 13 '10 at 14:15
1

Aaah... this is one of the exceptions which you really dont want to get in your application. We did get this exception too in our project and we used .net memory profiler to check the memory usages and leakages. To some extent we could reduce the frequency of getting outofmemory exception. Given below is the link for profiler -

http://memprofiler.com/

Sachin Shanbhag
  • 54,530
  • 11
  • 89
  • 103
  • I've found the SciTech profiler to be easier to use than ANTS. Worth a try. – spender Sep 13 '10 at 13:19
  • @spender - Thanks for this. We can try out on this new profiler too. Can you provide a link to this SciTech profiler? – Sachin Shanbhag Sep 13 '10 at 13:35
  • @spender - Its the same which we have used too. check the link in my answer ;) – Sachin Shanbhag Sep 13 '10 at 13:54
  • @Sachin: Crossed wires!. I was simply agreeing that your recommendation (and link) are good. I am 100% aware that the "SciTech profiler" and the profiler pointed to by the link in your answer are one and the same. ;) – spender Sep 13 '10 at 14:04
  • @spender - haha.. ok I thought they were different. thanks. Yeah we used this and it was really easy to use as you said... – Sachin Shanbhag Sep 13 '10 at 14:07
1

You need to identify what caused it, where it came from, and then you can look into rewriting that section so it uses less memory, maybe by breaking it into pieces and then freeing some resources.

Try introducing a log somewhere (doing step 1, doing step2) with timestamps to help identify where it's failing, then you can ask a more specific question about reducing the memory dependency if it isn't obvious.

Beth
  • 9,531
  • 1
  • 24
  • 43