Windows does overommit stacks. You can easily create a number of threads with stack space far beyond RAM + swap. The following C++20 program shows the page attributes of a one MB stack:
#include <Windows.h>
#include <iostream>
using namespace std;
int main( int argc, char **argv )
{
auto stackThread = []( LPVOID lpvThreadParam ) -> DWORD
{
ULONG_PTR stackTop, stackBottom;
GetCurrentThreadStackLimits( &stackBottom, &stackTop );
char const
*pStackTop = (char *)stackTop,
*pStackBottom = (char *)stackBottom;
SYSTEM_INFO si;
GetSystemInfo( &si );
MEMORY_BASIC_INFORMATION mbi;
auto query = [&]( char const *p ) -> char const *
{
if( VirtualQuery( p, &mbi, sizeof mbi ) != sizeof mbi )
ExitProcess( EXIT_FAILURE );
return (char *)mbi.AllocationBase;
};
char const *base = query( pStackBottom ), *p;
do
{
cout << mbi.RegionSize / si.dwPageSize << ": ";
unsigned n = 0;
auto append = [&]<typename ... T>( T &&... values ) { cout << (", " + !n++); ((cout << values), ...); };
if( mbi.State != MEM_FREE )
if( mbi.State == MEM_COMMIT )
append( "comitted" );
else if( mbi.State == MEM_RESERVE )
append( "reserved" );
else
append( "S: 0x", hex, mbi.State );
if( !mbi.Protect )
append( "unacessible" );
else if( mbi.Protect & PAGE_GUARD )
append( "guard page" );
else if( mbi.Protect == PAGE_READWRITE )
append( "read-write" );
else
append( "P: 0x", hex, mbi.Protect );
cout << endl;
p = (char *)mbi.BaseAddress + mbi.RegionSize;
} while( query( p ) == base && mbi.BaseAddress < pStackTop );
return 123;
};
HANDLE hThread = CreateThread( nullptr, 0x100000, stackThread, nullptr, STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr);
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
return 0;
}
This are the stack attributes shown on my computer:
251: reserved, unacessible
2: comitted, guard page
3: comitted, read-write
As you can see most of the stack is initially uncommitted.