SetProcessWorkingSetSizeEx() is not working on Windows Server 2022 - x64 OS.

We are planning to test and use SetProcessWorkingSetSizeEx() for setting limit on memory (working set) consumed by a service so that working set by our service never exceeds the limit set in dwMaximumWorkingSetSize parameter of this API. We are calling the API with QUOTA_LIMITS_HARDWS_MAX_ENABLE.

We are aware of the impact of this on System’s performance when the limit is exceeds as there can be a lot of page faults though the memory is available, depending on multiple factors. We are in the process of evaluating this, for evaluating we are setting 1/10th of Physical Memory Bytes as limit.

We tested this API on Windows Server 10 - x64, Windows Server 2019 - x64. The API returns success and the working set of our process never exceeds the limit.

And on Windows Server 2022 - x64, the API returns success but the limit is not honored by the OS. The working set by our process exceeds the limit.

As per the documentation of this API, minimum supported OS are

Minimum supported client Windows Vista [desktop apps | UWP apps]
Minimum supported server Windows Server 2003 [desktop apps

https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-setprocessworkingsetsizeex

Could anyone please help us on this, if anyone using this API? If there is any change in the behavior of this API on Windows Server 2022 OS.

If more details needed for understanding the issue, let me know. I am happy to share more details.

If this is not the correct forum could you please let me know the right forum in which I would get info?

Regards,
Arjun Annam

I don’t think that these limits have ever been enforced by any version of Windows. They have always been hints that the OS uses to help decide what to do. Letting any UM process set hard limits on what a virtual memory OS can do is an incompatible design philosophy. I don’t think that even VirtualLock has ever actually worked exactly as advertised, but I have not looked into this in a long time

The better question is why you care? Even if the limits did work, there is no way to control which pages are paged in or out. If you really want resident memory, then large pages are a much better choice, and if you want to limit the resources used by one process, then the only reason would be so that another process gets a chance - something that memory manager is in a better position to control anyways.

Thanks.

  1. The documentation of this API says
    “The QUOTA_LIMITS_HARDWS_MIN_ENABLE and QUOTA_LIMITS_HARDWS_MAX_ENABLE flags enable you to ensure that limits are enforced.”
    The limits are enforced with this flag and it is working on previous windows versions.

  2. Why we are doing this is that we want to set the max limit on the ‘working set’ by our process. We are okay if the pages are paged out and have an impact on process and analyzing the impact on the system. We are not looking for resident memory and not setting hard limit on minimum working set for process.

While I agree about the lack of wisdom of using it, Windows Internal V7 says QUOTA_LIMITS_HARDWS_MAX_ENABLE should set a hard limit.

Who knows if that’s how it works today….

yes, the documentation does say that, but think about how this must be implemented in practice.

These flags clearly have the effect of controlling whether the API will try to set or remove limits, or affect the current state of the process. Affecting the current state of the process is all about trimming pages, and won’t ever force pages to be loaded if the minimum is raised. Remember the old trick of minimizing a misbehaving application to prevent a system out of memory condition because the shell would call EmptyWorkingSet for applications as they lost focus? This documentation uses a system 64 MB of RAM as an example. Windows has not been able to run on a system like this for a long time

Let’s consider the working set minimum. There is no way that the OS can force the process to allocate enough memory to meet that minimum, so if it doesn’t, that limit can’t be enforced. Assuming that the application allocates at least that amount of memory, it can try to keep that much resident, but the page fault handler isn’t going to fail to service a page fault, and potentially crash the system, just to avoid evicting a page from this minimum resident set, so that limit is soft in both directions

let’s consider the working set maximum. Obviously this value should be larger than the minimum and if the process never allocates this much memory, then there will be no issue. But if it does, then the OS has to choose whether to trim these pages to to leave them alone. This is easier to implement than the minimum, but there are still issues. Remember that even though virtual address space is reserved and committed in advance, the physical memory (or page file space) is not allocated until actually accessed. Enforcing a limit by evicting less recently used pages when new ones are accessed while the system is not under memory pressure is not a desirable feature for the OS. So it is no surprise that this behavior changes from one OS version to the next, and that the limit is not hard no matter what the documentation says

So the question remains - what possible use is trying to set this limit? For the entire history of this API, it has always been about trying to get a minimum amount of memory allocated to a certain process, and the OS has always failed to agree completely. Too much memory assigned to any process has never been a serious concern unless is impinges on the ability of another process to get some. Different versions of Windows have approached this in different ways, but it can never really work

One possible use for this is to artificially reduce the resources available during testing / debugging in order to help find thread sync bugs by alter thread timing. Another possible use is to manifest such latent bugs in someone else’s code and cause malfunctions - malware. Another possible use is to limit a misbehaving application so that it won’t exhaust the resources on this system - but the OS will do this anyways and a really bad application can easily make the system unresponsive anyways. I should know because I have inadvertently done this more times that I care to admit. It is a bad thing when a machine with TB of RAM only keeps the mouse responsive because of a recycled IRP ;(

think about how this must be implemented in practice

There is exactly no point in getting into a debate while speculating on the facts. The OS does what the OS does… however it does it.

what possible use is trying to set this limit

The wisdom or lack thereof of using this API is left as an exercise to the reader. Maybe a user has a mandate to not use more than xMB of physical memory, on systems that are configured with a very small amount of memory. I certainly don’t know. While I think the use of the API is most likely to be unwise, the API is there, and documented, and people will use it.

The question is “What does it do in the most recent versions of Windows?” and that, I’m afraid to say, we don’t really know.

It is certainly true that there is little point in a debate based on conjecture. But In this case, the first principals tell us something about what the OS must be doing.

It is also true that this is a very old API and what it did in NT4 is certainly not what it does in Windows 11. I can’t immediately remember when it was introduced, and am too lazy to check, but I expect NT3.51 because working set is not really a thing until memory is virtual, and at the beginning it would have been very bold to assume that there was never a case for direct management by the application

My point is that whatever the motives of the OP are, this API could probably never meet the expectation on any version or hardware configuration. It was probably a fluke that it worked before for whatever purpose, and certainly should not be counted on in the future. Applications that need to control the amount of memory that they use, need to implement rigorous schemes, and applications that don’t - most applications - don’t need to do anything because the OS will fix it for them or kill them

Thanks, Peter. Thanks, MBond2.

My application is such that it’s memory usage depends on external factors, I am working on throttling the processing internally inside my application to reduce the memory usage. I am using another SetInformationJobObject() to set limit on CPU usage. Similarly, for worst case scenarios I am evaluating API SetProcessWorkingSetSizeEx() so understand if that helps in worst case scenarios and impact of using this API on system.

The resource requirements of most programs depend on external factors. Even programs like calc.exe or notepad.exe need more or fewer resources depending on what the user does, and certainly every network aware program has this problem. The question is what’s the use in artificially reducing the resources that are available? Why not just let your program use all of the CPU cycles and all of the RAM?

Usually, it is one of two reasons:

  1. I want to hold something back for another process; or
  2. The OS is too generous and lets my program allocate too much until the whole system crashes

It is worth noting that the use of a hypervisor and virtual machines can be an effective way to isolate conflicting workloads

Internally, SetProcessWorkingSetSizeEx() just uses NtSetInformationProcess() with ProcessQuotaLimits.

For example: NtSetInformationProcess(hProcess, ProcessQuotaLimits, &QuotaLimits, sizeof(QuotaLimits));

It also needs to acquire SE_INC_BASE_PRIORITY_PRIVILEGE for this.

I would start calling this directly avoiding WINAPI and go from there.