Reputation: 3618
I need to allocate lots of memory to emulate memory consumption by .NET app. I've expected that new byte[1000*1024*1024]
would allocate all memory at once, but it is not happening.
For example, consider following code:
var bytes = 1000*1024*1024;
var memory = new byte[bytes];
memory[bytes - 1] = 16;
//Step 1
Console.ReadLine();
for (int i = 0; i < memory.Length / 2; i++)
{
memory[i] = 16;
}
//Step 2
Console.ReadLine();
for (int i = memory.Length / 2; i < memory.Length; i++)
{
memory[i] = 16;
}
According to Process Explorer, no memory is allocated till Step 1, and before Step 2 only half of the expected memory is allocated. Only after Step 2 all 1000*1024*1024 bytes are allocated. Compiled both in Debug and Release VS configurations.
So the questions are:
UPDATE:
I've inspected the memory consumption via Resource Monitor tool, and the "Commit" section shows the correct 1000 Mb, however "Working set" behaves as I've described above. As my task is to emulate real load, I suppose I need to load actual physical memory, not virtual one.
Upvotes: 1
Views: 407
Reputation: 942187
This is standard behavior on a demand-paged virtual memory operating system like Windows. Allocating memory with the new operator only allocates address space. Just numbers to the processor, one for every 4096 bytes. A page.
The commit size grows, you have a solid promise that you can get the physical storage you need once you start addressing the array elements. Windows has reserved space in the paging file for your allocation.
But the RAM allocation doesn't happen until you actually start using the array elements. That causes a paging fault, you'll get one for every 4096 elements. The operating system is now forced to make the physical storage available. RAM.
That tends to increase the working set, the amount of RAM your program uses. That is not guaranteed to happen, when the operating system is under pressure and needs to distribute the available RAM across memory-hungry processes then it may need to forcibly discard a page to make it available to your program. Typically just throwing the content away when the page contains code or is mapped to a file (like code is). Or it backs up the content to the paging file. To be retrieved again, later, when the process needs to have the page available in RAM again.
The kind of page faults you normally get for new allocations are soft page faults. They are dealt with quickly, the operating system pulls a page of RAM from a pool. You get a hard page fault if another page needs to be discarded and written to the paging file or your page was swapped out and needs to be swapped back in. That can slow a program down significantly, a problem called "thrashing". Machines that don't have enough RAM and are overloaded tend to be slow due to thrashing.
A consequence of this operating system design is that there is no real difference between a file and memory anymore. When you use memory then you also use a file. The paging file usually. And the other way around, when you use a file then you actually use memory. The file system cache. RAM is just a convenient way to avoid having to access the disk. The more RAM the machine has, the less often you'll hit the disk. Programmers tend to fret too much over this, trying too hard to keep data in memory when they also have a choice (or no given choice by an api) to use a file. And get into trouble like OutOfMemoryException or causing thrashing.
Upvotes: 2
Reputation: 209935
I tried this program in LinqPad while having ProcessExplorer open. It indeed increases memory right at step 1, but only the private bytes (which is virtual memory). The working set stays more or less the same size. The reason for this is that private bytes here represents virtual memory. That's allocated on-demand, but won't be backed by physical memory until needed. The working set represents actual physical memory used. Once you start using the array, the parts of it that were not allocated in physical memory will be allocated and the working set size will increase. Notice that it probably won't reach the full size of the array since that would be inefficient. Instead, the parts that haven't been touched in a while will be swapped out so that the working set doesn't grow to an enormous size.
EDIT: in answer to your update, you have few options:
Upvotes: 2