Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explain a line of two's complement math counting free virtual memory

I have a Visual Studio 2008 C++ application for Windows Mobile 6.x where I'm counting the amount of free virtual memory available for a given process. (I realize it is not taking fragmentation in to account.) My code looks basically like this:

MEMORY_BASIC_INFORMATION mbi = { 0 };

/// total free memory available to the process
DWORD free = 0;

/// base memory address for the given process index (2-33). 
DWORD slot_base_addr = process_index * 0x02000000;

/// look at each memory region for the process. 
for( DWORD offset = 0x10000; 
     offset < 0x02000000; 
     offset += mbi.RegionSize )
{
    ::VirtualQuery( ( void* )( slot_base_addr + offset ), 
                    &mbi, 
                    sizeof( MEMORY_BASIC_INFORMATION ) );

    if( mbi.State == MEM_FREE )
    {
        free += ( mbi.RegionSize - ( ( ~( DWORD )mbi.BaseAddress + 1 ) & 0xffff ) ) & 0xffff0000;
    }
}
NKDbgPrintfW( L"%d bytes free\r\n", free );

I can confirm with other APIs that this seems to work perfectly. My question is what this line is doing:

free += ( mbi.RegionSize - ( ( ~( DWORD )mbi.BaseAddress + 1 ) & 0xffff ) ) & 0xffff0000;

Why is this not just:

free += mbi.RegionSize;

I found the former line on a Usenet post by MSFT employee Ross Jordan.

Thanks, PaulH


Edit:

For example. For process slot 2, this is a list of each free memory block with the amount of free memory given by both the Ross Jordan (RS) algorithm and just the RegionSize (RS).

Slot: 2. Range: 0x04000000 - 0x06000000
    RS:    16,384 bytes RJ:         0 bytes diff: 16384
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:    36,864 bytes RJ:         0 bytes diff: 36864
    RS:    65,536 bytes RJ:    65,536 bytes diff: 0
    RS:    53,248 bytes RJ:         0 bytes diff: 53248
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS:     4,096 bytes RJ:         0 bytes diff: 4096
    RS: 7,671,808 bytes RJ: 7,667,712 bytes diff: 4096
    RS: 1,921,024 bytes RJ: 1,900,544 bytes diff: 20480
    RS: 7,491,584 bytes RJ: 7,471,104 bytes diff: 20480
    RS: 3,252,224 bytes RJ: 3,211,264 bytes diff: 40960
    RS:   262,144 bytes RJ:   262,144 bytes diff: 0

RS: Total VM Free: 20,811,776 bytes.
RJ: Total VM Free: 20,578,304 bytes.

Edit 2:

Hans led me to the answer. It's just a fancy way of doing this, but assuming that the allocation size is 64KB.

SYSTEM_INFO si = { 0 };
::GetSystemInfo( &si );

free += mbi.RegionSize - mbi.RegionSize % si.dwAllocationGranularity;
like image 678
PaulH Avatar asked Nov 05 '22 07:11

PaulH


1 Answers

Allocation granularity for VirtualAlloc is normally 64KB. He tries to do something meaningful if the AllocationBase is not a multiple of 64KB. I don't think it is meaningful at all, his bitmasks still assume a granularity of 64KB and he doesn't use SYSTEM_INFO.dwAllocationGranularity. Which has this comment:

This value was hard coded as 64 KB in the past, but other hardware architectures may require different values.

In the very rare case where it is not 64KB, this code would generate junk values. Just zap it, go by RegionSize.

like image 136
Hans Passant Avatar answered Nov 09 '22 08:11

Hans Passant