While testing some things regarding page faults I discovered a curious difference between how new
operates in Debug mode and Release mode in MSVC. Consider the following code1:
#include <array>
constexpr size_t PAGE_SIZE = 4096;
int main()
{
const size_t count = 1000000;
char* const mem = new char[PAGE_SIZE * count];
// page align the pointer, c-style casts used for brevity
auto* pages = (std::array<char, PAGE_SIZE>*)((size_t)mem - (size_t)mem % PAGE_SIZE + PAGE_SIZE);
for (int i = 0; i < count; ++i)
pages[i][0] = 'a';
}
The code allocates a million normal memory pages on most architectures. It then physically writes to this allocated memory, so the memory really has to be "given"2 to the program - not merely "reserved" for it in some way. The curious thing is, when this actually happens. To investigate this, I stepped through the code using the Visual Studio debugger and looked at the memory usage graph in Task Manager. The results are below:
The red time point is the program being launched, the green time point/interval is the call to new char[]
, the blue time point/interval is the for
loop.
As it turns out, in Debug mode, new
both "reserves" and "gives" memory to the program. Meanwhile, in Release mode, it only "reserves" it, as the memory is "given" by the loop. I expected only the behavior present in Release mode - I thought that the memory is "given" to the program only when a page fault occurs.
Why does new
behave in this way? Does this have any significant implications?
1 By the way, for some reason changing auto* pages
to auto* const pages
causes an Internal Compiler Error.
2 I am a bit confused regarding the correct terminology, so I used "given" and "reserved" instead.
To understand what happened you need to know two things:
The combination of points 1 and 2 mean the debug version of new
acquires the memory and immediately accesses it by writing in the uninitialized memory detection pattern and forcing the system to find and hand over real memory in the green region. As an added bonus if the computer does run out of physical storage, the program will likely crash here and not some seemingly random point in the future when the request cannot be satisfied.
The release version of new
does not do point 1, so physical memory acquisition is deferred as per point 2. new
exits the green region quickly without any physical memory. If some or all of the requested memory is never used, the computer profits by never having to do the work fulfilling the request. The program does use the requested storage in the for
loop, so the system is forced to find and supply physical memory in the blue region.