5

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;
PaulH
  • 7,759
  • 8
  • 66
  • 143

1 Answers1

2

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Something in there is important. The available memory for the 'current process' given by that algorithm matches what `GlobalMemoryStatus()` says it is. If I just go by `RegionSize`, the result is consistently smaller by a couple dozen KB. For reference, my dwAllocationGranularity is 64KB. – PaulH Nov 01 '10 at 15:11
  • Erm, how is that possible? The statement *subtracts* from the RegionSize. It can only make it smaller, not bigger. If you like GlobalMemoryStatus, maybe you should just use that. – Hans Passant Nov 01 '10 at 15:17
  • +1, Windows mobile is quite liable to be one of thes 'other hardware achitectures' – SmacL Nov 01 '10 at 15:19
  • @Hans - I misspoke (as you noticed). The RJ algorithm is a couple dozen KB smaller than the RegionSize. GlobalMemoryStatus() is fine to find the free memory for the process in slot 0 (the 'current' process). I need the free memory for the other processes. Also, expanding on the above algorithm will let me find the largest contiguous block of free memory which GMS() won't give me. See my edit. – PaulH Nov 01 '10 at 16:18
  • Well, sounds to me like you are convincing yourself that making corrections for BaseAddress is what you really want to do. Maybe some kind of quirk of Mobile, I have no way to check. – Hans Passant Nov 01 '10 at 16:26
  • I'm convinced because that algorithm agrees to the byte with GlobalMemoryStatus(). I just want to understand _why_ it's necessary. It may well be just a quirk of WM. I appreciate your help. – PaulH Nov 01 '10 at 16:32
  • @Hans - I think I've got it figured out. You were right about the dwAllocationGranularity being the key, I think. Since you must allocate in chunks of size `dwAllocationGranularity`, it throws away anything that's less than that or not divisible by that because it's technically useless to you. Please, take a look at edit 2 and let me know if that make sense to you. – PaulH Nov 01 '10 at 16:54