ITQuants blog

Sophis Risque: how to manage/limit memory allocation?

Feb 6

Written by:
2/6/2014 6:59 PM  RssIcon

Even on x64 OS environment, it is not interesting to let the process using all the available memory. In some cases, like on CITRIX servers, the fact that one user consumes a lot of quantity will impact all users too. The goal of this post is to analyze all the possibilities that gives the MS SDK, in order to capture either bad memory allocation or to limit the process to a certain quantity of RAM.

Concerning the limitation to a certain quantity of RAM, it is possible to restrict by using the MS IO counters. The following code works fine in C++. It is possible to do same directly in C# by using PInvoke, or by encapsulating it in a native dll and to call it through a managed C++ class:

HANDLE jobHandle = ::CreateJobObject(NULL,NULL);
if (jobHandle != NULL)
{
    size_t max_memory = size_t(1024)*size_t(1024)* (size_t(1024)*size_t(4)) ; //4.0Giga bytes
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtLimit;
    memset(&jobExtLimit,0,sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
    jobExtLimit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; 
    jobExtLimit.ProcessMemoryLimit = max_memory;
    jobExtLimit.PeakProcessMemoryUsed = max_memory;
    jobExtLimit.JobMemoryLimit = max_memory;
    jobExtLimit.PeakJobMemoryUsed = max_memory;
    if (::SetInformationJobObject(jobHandle,JobObjectExtendedLimitInformation,&jobExtLimit,sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)))
    {
    if (!::AssignProcessToJobObject(jobHandle,::GetCurrentProcess()))
    {
        DWORD errorCode = ::GetLastError();
        // TODO: some log
    }
    }
    else
    {
    DWORD errorCode = ::GetLastError();
// do some log again
  
....

 

Calling this code will restrict the process to 4GB. It means that if this limit is reached, the process will exit suddenly, without prevent the end user. The best is to use an environment variable that could be linked to some Sophis user too. Calling this method in UNIVERSAL_MAIN is something which makes sense. If batches have not be limited, methods on CSRApi that will give in which mode the toolkit is used can be called (CSRApi::IsInGUIMode).

Whatever, in some cases, Sophis hangs at the end, when the memory allocation fails. In these cases, in order to be sure to terminate and exit, it is possible in C++ to overload the the catch of the memory allocation exception by using for example this code in some toolkit dll:

#include
...
static _PNH _oldCallback = NULL;
int HeapAllocFailure(size_t size)
{
    __try
    {
        TerminateProcess(GetCurrentProcess(),-2);
    }
    __finally
    {
    }
    return NULL;
};
  
  
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        _oldCallback = _set_new_handler(HeapAllocFailure);
        _set_new_mode(1);   
        break;

 

Indicating 1 in the _set_new_mode method permits to override the exception in the new and malloc functions. It won't be called in case of memory allocation exception that occurs in C# code, if some assembly code makes memory allocation. The .Net engine has its own exception handling. Catching a problem of memomry allocation in C# is possible using the try/catch(OutOfMemoryException ) mechanism, but it should be placed in the C# code.

Search blog