Home > Software design >  Operator new behaves differently in Debug mode than in Release mode in MSVC
Operator new behaves differently in Debug mode than in Release mode in MSVC

Time:01-25

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 Measurement results

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.

CodePudding user response:

To understand what happened you need to know two things:

  1. The debug builds do a lot of cool stuff for you to help you find bugs. One is writing a known value into the program's memory so you'll more easily recognize that you've messed around with uninitialized storage.
  2. Modern memory management systems in CPUs are complicated, but one thing they all tend to do is as little as possible until they have to. When a program requests storage the underlying system checks that there is enough virtual addressing space and then almost always allows the request without filling it. No physical memory is found and assigned to the virtual memory. When the memory is accessed, then physical memory will be found and assigned or the program fails because memory was not available.

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.

CodePudding user response:

Debug new fills memory with recognizable pattern.

  •  Tags:  
  • Related