startwithmemlimit

At first I couldn’t believe that such tool wasn’t readily available because I’ve seen dozens of posts on different forums asking how to do this, however… I guess nobody felt urged enough to actually write a command line app for this. Well, I did 😉 startwithmemlimit is a Win32 application, which can impose a hard limit on the maximum amount of virtual memory a process (and *by default* its children) can allocate. It uses Windows Jobs mechanism, available since Windows 2000. You can read a lot about it on Google and on MSDN, e.g. here, here and here. I prepared the binaries with 32- and 64-bit compilers provided with Windows SDK. You can also recompile them yourself if you’d like 😉 The code is BSD-licensed as always.

Usage:

startwithmemlimit32.exe command_line mem_limit_in_MB
startwithmemlimit64.exe command_line mem_limit_in_MB

DOWNLOAD:
startwithmemlimit.zip

Source code (BSD-licensed):

#define _WIN32_WINNT 0x05000
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class ScopedHandle {
public:
    ScopedHandle(): m_Handle(NULL) {}
    ScopedHandle(HANDLE h): m_Handle(h) {}
    ~ScopedHandle() { if (m_Handle != NULL) CloseHandle(m_Handle); }
    ScopedHandle& operator=(HANDLE h) { m_Handle = h; return *this; }
    bool operator==(HANDLE h) const { return (m_Handle == h); }
    operator HANDLE() const { return m_Handle; }
private:
    HANDLE m_Handle;
};

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("Usage: startwithlimit command_line memory_limit_MBn");
        return -1;
    }
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    if (!CreateProcess(NULL, argv[1], NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
        printf("Failed to create processn");
        return -2;
    }
    ScopedHandle hProcess = pi.hProcess;
    ScopedHandle hThread = pi.hThread;
    ScopedHandle hJob = CreateJobObject(NULL, NULL);
    if (hJob == NULL) {
        printf("Failed to create jobn");
        return -3;
    }
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION eli;
    if (!QueryInformationJobObject(hJob, JobObjectExtendedLimitInformation, &eli, sizeof(eli), NULL)) {
        printf("Failed to query job infon");
        return -4;
    }
    eli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY;
    eli.JobMemoryLimit = _atoi64(argv[2]) * 1024 * 1024;
    if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &eli, sizeof(eli))) {
        printf("Failed to set job infon");
        return -5;
    }
    if (!AssignProcessToJobObject(hJob, hProcess)) {
        printf("Failed to assign process to jobn");
        return -6;
    }
    if (ResumeThread(hThread) == -1) {
        printf("Failed to resume processn");
        return -7;
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *