How to allocate address space with a custom alignment or in a custom address region

Raymond Chen

Windows 10 version 1803 added the ability to request specific alignment and address region requirements when creating address space mappings either for virtual memory (Virtual­Alloc2) or file mappings (Map­View­Of­File3). These new functions let you pass a MEM_ADDRESS_REQUIREMENTS structure which can specify additional constraints:

  • Lowest­Starting­Address: The lowest address in the region.
  • Highest­Ending­Address: The highest address in the region.
  • Alignment: The desired alignment.

If any field is zero, then the system uses whatever value it normally would have. For example, if you say that the alignment is zero, then the memory will be aligned on the allocation granularity (for normal-sized pages) or the large page granularity (for large pages).

Here are some sample usages:

Scenario Lowest­Starting­Address Highest­Ending­Address Alignment
Align to multiple of N 0 0 N
Below 4GB 0 0x00000000`FFFFFFFF 0
Above 4GB 0x00000001`00000000 0 0
At 1MB boundary
between 2GB and 4GB
0x00000000`80000000 0x00000000`FFFFFFFF 0x00000000`00100000

Note that the Highest­Ending­Address is endpoint-inclusive. In other words, it is the highest address in the region, not the highest address plus one.

Here’s how you pass the MEM_ADDRESS_REQUIREMENTS:

MEM_ADDRESS_REQUIREMENTS requirements = {0};

requirements.LowestStartingAddress = ⟦ value ⟧;
requirements.HighestStartingAddress = ⟦ value ⟧;
requirements.Alignment = ⟦ value ⟧;

param.Type = MemExtendedParameterAddressRequirements;
param.Pointer = &requirements;

auto result = VirtualAlloc2(nullptr, nullptr,
        &param, 1);

The Virtual­Alloc2 function takes a pointer to an array of MEM_EXTENDED_PARAMETER structures, but since we have only one, we just pass the address of a single object and tell the system that we have an array of only one element.

Note that you cannot combine non-default MEM_ADDRESS_REQUIREMENTS with an explicit Base­Address. If you pass an explicit base address, then you are saying, “I want the memory to be exactly here; I don’t want the system to choose for me.” Under those conditions, it’s meaningless to tell the system “And here is how I’d like you to choose the address for me.”


Comments are closed. Login to edit/delete your existing comments

  • Declan 0

    Hi Raymond, I had a question and I wasn’t sure if there might be a better place to ask it.

    I know that you’ve commented before on some of the internal aspects of the global memory manager and how it validates pointers to functions like GlobalLock. I noticed though, that when you GlobalLock a pointer, it only increments the lock count until it reaches 255, but subsequent calls to GlobalLock still return a pointer to the memory block. I don’t think that this is something that would have any noticeable impact, but I’m curious to know if there’s any significant reason as to why it is the way it is.

    I apologize if this is off-topic for the article.

  • Derp McDerperson 0

    I requested a function like this 2 decades ago. It’s nice to see that Microsoft finally implemented it.

    They half-baked what I requested though:

    1. There should be a way to specify a “preceding allocation”, i.e. lets say you want something aligned on 1 MiB boundary but want a 4 KiB block preceding it it.

    2. There should be a way to specify that the end address of the block be aligned as opposed to the begin address of the block be aligned.

Feedback usabilla icon