问题
In a memory hungry app I'm developing I'm facing OutOfMemoryExceptions. I expected to never have this kind of exceptions with virtual memory. Why if it is needed more RAM than available the OS is not using the HD as RAM?
回答1:
You can get an OutOfMemoryException in many different ways. In 32-bit windows, a process can use 2GB of virtual address space (I believe it's 1TB in 64-bit applications). Keep in mind this is not RAM, it is completely different, hence "virtual". So forget about RAM. All that RAM does is make things quicker. The windows memory manager decides if the memory your program is using will be in RAM or disk. When you have lots of programs open, less memory is loaded into physical RAM and more will be on disk. Your performance will be terrible, but you won't have an OOM exception.
The most common way to get an OOM exception in .NET, is to try to allocate a large enough amount of data (something like a byte array) where there is not enough contiguous pages of memory available to map it, which is where I suspect your problem lies. This means that it's possible to get an out of memory exception, even if you're requesting a new object that would increase the virtual address space to an amount over 2GB.
"But wait!", one might say, "When the garbage collector runs, the memory is compacted, so there's always contiguous space at the end of the heap!"
That is true, for the most part. The GC will collect from the heap, which can really be thought of as two heaps, the Small Object Heap (SOH) and them from the Large Object Heap (LOH). Most objects, since they're fairly small, will live in the main generational heap. However, if the object is above a certain size threshold (this was 85K, but may have changed), it is instead allocated on the LOH.
When memory is reclaimed from the LOH, it is not compacted like the SOH. It would be far too expensive for the memory in the LOH to be compacted. Memory allocation is expensive enough as it is, and application performance would suffer. This means that your heap can and often does, become fragmented (this has been improved greatly in .NET's 4.0 runtime, vs 2.0 compatible versions).
- Let's say your LOH heap looks like this prior to a GC:
* = object - = free space |**|***|*|**|*|******|****|**|*****|*******|**|*|***|*****|***|**|-- ^ ^ ^ ^ ^ ^ ^ marked for GC
- it will look like this once a full GC has been done:
* = object - = free space |--|***|*|-- -|******|****|**|-----|*******|--|*|***|-----|***|----
Now I want to allocate an array ********
long. The runtime is going to try to find a spot in there where it can fit. It doesn't. What does the runtime do? Well, assuming that the above represents your entire 2GB virtual space and no more memory can be allocated, an OOM exception will be thrown. You have free pages of memory, but not enough that are contiguous.
I have no idea what your application does, but here are a few things to check:
Are you allocating large array in static type members? As long as the static member points to that object, it will never be GC'd, even if you don't use it again. Static members live on the type object, and they are only freed when the AppDomain is unloaded.
Are you getting a allocation size based on user or other input? If, for example, you are reading a file header that tells you the length of an entry, are you verifying that the size of the entry is correct? I've run into off-by-one errors myself, I've read the wrong address in a file and tried to allocate 2TB of memory. Verify that madness.
If you are allocating large arrays, do you really need to? Loading data into memory doesn't guarantee that your performance will increase. Keep in mind your program is allocated virtual memory space, not ram. If you are reading a file, are you picking and choosing which ones you read completely into memory and which ones you're streaming?
There's are just a few things I can think of, and it's certainly not comprehensive. Another way to run out of memory besides trying to address more than 2GB of address space would be a full page file, but that's less common.
Some helpful tools:
WinDbg (included in the Windows SDK, as part of debugging tools for windows) - You can use this to view statistics about the heap. A personal favorite of mine is
!dumpheap -stat
and!dumpheap -type [type]
. I can use that to quickly find types that are using a lot of memory.CLRProfiler - Easier to use than WinDbg, with less features. It gives you a great graphical look at memory allocations, and you can even see how many GC handles were left when the application exited. The graph is colored by type, so it gives you an overview of what types are using the most memory in your heap.
回答2:
You can get this if you exceed the maximum heap size, or address space.
来源:https://stackoverflow.com/questions/9610552/how-can-i-get-an-outofmemoryexception-in-net