Windows Job Object: Can a rejected memory commit increase PeakJobMemoryUsed beyond JobMemoryLimit?

Abdourahmane Gueye 0 Reputation points
2026-10-29T07:30:08.4033333+00:00

Windows Job Object: Can a rejected memory commit increase PeakJobMemoryUsed beyond JobMemoryLimit?

On Windows, we are testing a Job Object configured with both JOB_OBJECT_LIMIT_JOB_MEMORY and JOB_OBJECT_LIMIT_PROCESS_MEMORY.

The job-wide hard limit is 536,870,912 bytes.

Immediately before a VirtualAlloc(MEM_RESERVE | MEM_COMMIT) request, PeakJobMemoryUsed is 528,871,424 bytes.

A subsequent 16,777,216-byte commit request fails with error 1455, and the completion port reports JOB_OBJECT_MSG_JOB_MEMORY_LIMIT.

The allocating process's private committed memory remains unchanged.

However, PeakJobMemoryUsed subsequently reports 545,681,408 bytes—8,810,496 bytes above the configured hard limit.

The limits were configured before the workers were assigned and resumed, and the peak was measured before cleanup.

Questions:

Can an unsuccessful memory-commit attempt temporarily contribute to PeakJobMemoryUsed, even when the hard Job Object limit prevents the commit?

Is PeakJobMemoryUsed guaranteed never to exceed JobMemoryLimit when the hard limit is enabled?

What supported Windows measurement should be used to distinguish successfully committed job memory from transient accounting associated with a rejected allocation?

We're looking for documented Windows kernel accounting semantics, not a workaround that disables the memory limit.

Windows development | Windows API - Win32
0 comments No comments

1 answer

Sort by: Most helpful
  1. Percy Nguyen (WICLOUD CORPORATION) 240 Reputation points Microsoft External Staff Moderator
    2026-10-30T02:25:03.08+00:00

    Hi @Abdourahmane Gueye ,

    Thanks for posting your questions.

    The documented hard-limit behavior is clear, but the documents don't say how the peak counter is updated during a failed allocation.

    JOB_OBJECT_LIMIT_JOB_MEMORY specifies that an attempt to commit memory beyond the job-wide limit fails. If the job is associated with a completion port, a JOB_OBJECT_MSG_JOB_MEMORY_LIMIT message is sent. Receiving that message therefore doesn't mean the allocation succeeded. JOBOBJECT_BASIC_LIMIT_INFORMATION For your three questions:

    1. Can a rejected commit raise the peak? Your observations are consistent with the commit being charged provisionally, updating the peak, and then being rolled back. However, I can't cite public Microsoft documentation that establishes that sequence. It should be treated as a possible explanation of the implementation, not a documented Windows guarantee.
    2. Must PeakJobMemoryUsed stay at or below JobMemoryLimit? The Remarks say that, with a job-wide limit, the job's processes "could never exceed" the limit. That statement is about committed memory. It doesn't say how the PeakJobMemoryUsed counter treats rejected allocations, and no counter invariant is documented. The field is described only as peak memory usage, tracked constantly. So an elevated peak alone doesn't prove that an over-limit commit succeeded. The docs also don't establish that this overshoot is intended behavior. JOBOBJECT_EXTENDED_LIMIT_INFORMATION
    3. What should be measured? For private VirtualAlloc allocations, call GetProcessMemoryInfo and read PROCESS_MEMORY_COUNTERS_EX::PrivateUsage before the call and after it returns. PrivateUsage is documented as the process's commit charge. For a total across the job, enumerate the job's processes and add up those values while the workers are idle. This gives the job's total private commit at each measurement point. It isn't an atomic job-wide snapshot or an exact historical peak. PROCESS_MEMORY_COUNTERS_EX

    Be careful with JOBOBJECT_LIMIT_VIOLATION_INFORMATION::JobMemory. It's filled in only when a notification limit is violated, and it reports committed memory at the time the notification was sent. It isn't documented as an always-available counter of current memory for hard-limit failures. JOBOBJECT_LIMIT_VIOLATION_INFORMATION

    Your peak increased by exactly 16 MiB + 32 KiB. That's suggestive, but the starting value was a historical peak. So the arithmetic establishes neither the charge at the moment of the request nor the reason for the extra 32 KiB.

    Confirming the actual accounting sequence would need a kernel trace on the affected Windows build or clarification from Microsoft. The public API documentation doesn't cover that implementation detail. If you need an authoritative answer for your build, you can open a case with Microsoft Support through Support.

    If you found my support helpful, I’d really appreciate it if you could follow this guide so others with similar issue can benefit as well.       

    Thank you.   

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.