December 29th, 2025
heart1 reaction

How can I detect that the system is running low on memory? Or that my job is running low on memory?

A customer wanted to write a process that could detect that it is nearing its job memory limit.

Now, if you were asking about system memory limits rather than job memory limits, you would use the Create­Memory­Resource­Notification to create a “low memory” notification. As noted in the documentation, this notification is delivered to all programs simultaneously. It talks about the system-wide memory situation, ignoring any additional memory limits imposed by your process being in a job.

They asked a large language model for help, and the model provided some very nice code that was based on wishful thinking: It produced a window procedure that responded to the WM_LOW­MEMORY message. Too bad there is no such message.

I mean, if we’re going to allow code based on wishful thinking, then everybody should just replace their program with a single function call to Do­My­Work­For­Me().

For the case of a process discovering that it is nearing or has reached its memory limit, the news is not promising.

If you have JOB_OBJECT_SET_ATTRIBUTES access to a job (which you get automatically when you call Create­Job­Object), you can set its Job­Object­Notification­Limit­Information and Job­Object­Notification­Limit­Information2 to specify the points at which the job will generate a limit violation notification. (A job that exceeds these limits continues to run. This is just the point at which the system will generate a notification.) When the limit is reached, the job object delivers a JOB_OBJECT_MSG_NOTIFICATION_LIMIT completion to its associated completion port.

The first catch is that for a process to monitor notifications for itself, it needs a handle to the job object that contains it. And with the introduction of nested jobs, it’s possible that there is more than one such job object, so you have to get the one whose limit you are interested in.

The second catch is that the notification is delivered to the job object’s completion port, and a job object can be associated with only one completion port at a time. And the code that created the job object has probably already attached the job to its own completion port so that it can receive these very notifications from it.¹

The system does not have a way for a process to retrieve a handle to its enclosing job. This would be like providing a way for a prisoner to get the keys to their own cell. If you want a process to get a handle to its own job, you’ll have to arrange for it yourself. For example, the job creator could mark the job object handle as inheritable and allow it to be inherited by the child process. Or it could duplicate the job object handle into the child process directly. But it has to be your idea to give the prisoner the keys to their own cell.

Still, even with the job object handle, the child process can’t listen to the job creator’s completion port.² The job creator would have to forward the notifications to the child process by some custom mechanism.

The original philosophy of job objects was that the processes inside a job have been placed by the job creator on a type of double secret probation. They don’t know that their actions are being monitored or that they are operating under any unusual constraints. They just go about their usual business, but if they step out of line, then boom, the job object banhammer comes down. Giving a process the ability to access the job that constrains it would negate the whole “double-secret” nature of job objects.³

¹ Completion ports cannot be shared between processes because I/O completions contain pointers, and pointers are not valid across processes.

² The child process could reassociate the job object with a new completion port that belongs to the child process, though this would be a rather rude thing to do to somebody you are presumably cooperating with, since that means that the job creator no longer receives notifications about the job.

³ Over time, a few small places have been created by which a process can respond to being placed in a job object. The CREATE_BREAKAWAY_FROM_JOB flag for the Create­Process function lets a process say, “If I am running in a job, then this process that I’m creating should be placed outside the job.” Whether that request is honored is another story, but the existence of the flag itself betrays the possibility.⁴

And a process can use Is­Process­In­Job to detect whether it is on probation, so it’s not double-secret any more, though the only question you can ask is “Am I on probation?” You don’t find out what the terms of the probation are.

⁴ It’s like if you hear rumors about a Silver Star Pass, but everybody you ask just says, “Sorry, I don’t know what you’re talking about.” But then you find deep in the fine print or some document a sentence like “This restriction does not apply to Silver Star Passes.” You don’t know what a Silver Star Pass is, but at least now you know that it exists, or at least existed at the time that text was written.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments