Under what conditions could a commit of reserved memory fail?

A customer's program reserves a large chunk of address space (Virtual­Alloc with MEM_RESERVE) and commits memory into it as necessary (Virtual­Alloc with MEM_COMMIT). They received crash reports that indicated that the commit was failing, which leads to the program reporting a fatal error. Unfortunately, in between the failed Virtual­Alloc and the code that generated the fatal error, the error code set by Virtual­Alloc was lost. (The customer sheepishly acknowledged that this was due to "poor code".)

The customer wanted to know what situations could result in the failure to commit memory that had previous been successfully reserved.

The obvious reason is that you are out of memory. The system will satisfy commit either from physical memory or from the page file.¹ The page file will not extend indefinitely, so you will eventually run out of memory if you keep committing.

A less obvious reason is that the process may be running inside a job that has a commit limit.

Once the customer knew what to look for, they were able to verify that the program running into the problem was indeed running inside a job. Furthermore, the job memory limit was set to 155MB, which is low for the type of work the program normally performs.

Mystery solved. Of course, the next mystery is why the program is running in a job with a low commit limit, but the customer at least knew what direction to proceed next.

¹ It's not quite that way. The system does not assign a physical page or a page file page to you when you commit.² The system merely does enough bookkeeping to ensure that if you write data to the pages that you committed, then the system will have a place to hold this data and produce it upon demand. That place might be physical memory, or it might be the page file, but the system doesn't know at commit time which it will be (and it may end up being both at different times).

² This delay-assignment of committed pages means that reserving address space and then committing it as needed doesn't save you any physical memory. The commit doesn't require physical memory. The physical memory doesn't get assigned until you try to access the memory. All you're saving is system commit, which is just a number (although as noted in the previous footnote, the number does have a maximum value). Unless you are doing this to save large amounts of memory (dozens of megabytes or more), committing on demand is usually not worth the effort.

Comments (4)
  1. kantos says:

    My understanding was always that reserving memory was intended to be used by custom malloc implementations as a way of keeping a contiguous heap; or for (potentially) large dynamic allocations where continuity in the address space was needed. Basically a way of telling the loader to not load anything there because you’re going to potentially need it as one big block later. In fact based on MSDN and this blog I’ve always assumed that the memory manager could care less about reservations for the most part until you committed.

    1. JDG says:

      *could not

  2. Someone says:

    ” was intended to be used by custom malloc ” ?

    Why only *custom* mallocs? The use of Memory Mapped Files as also loading&unloading of additional DLLs after initial startup can fragment the address space at any time. So every implementation of heap memory should reserve “some” address space (at least on 32bit systems). But its tricky to leave enough space for other heaps and mapped files….

    1. kantos says:

      I assumed HeapAlloc already does this, as far as I know all the *Alloc allocators on windows are implemented in terms of VirtualAlloc at the lowest level as it is the WIN32 closest equivalent of mmap.

Comments are closed.

Skip to main content