Set process memory limit
Is there a way to have mimalloc enforce an allocation limit?
I am looking at using mimalloc in a docker image. I would like to use it rather than docker -m. When using -m when the process exceeds the limit it is terminated. In some instances my data analytic process might try to allocate large blocks of memory, so instead of terminating i would rather error trap and keep the process alive.
Currently mimalloc has no limit built in -- but it is an interesting idea! especially as mimalloc could nicely start returning NULL pointers while still having OS memory available for its own structures. I'll look into it to see how easily this could be done.
@daanx I started down my own path and just created a LD_PRELOAD that interrupts malloc/calloc/free/realloc/mmap/munmap/mremap and just tracking allocations and when exceeds an environment variable setting it starts returning NULL and setting errno = ENOMEM i did run into some situations where we need to allow some small allocation to happen once we exceeded to allow the stacks and threads to unwind, but its working pretty well.
I am just using the built in c runtime function but it would be cool to see this added as a feature to mimalloc. I'm too late in the game to switch to mimalloc at this time
Thanks @arsnyder16 . I'll update this issue if I get this functionality integrated; seems a good thing to have anyways. I may integrate this together with better "global" statistics.
@arsnyder16 do you have that LD_PRELOAD available somewhere publicly?
@mikroskeem Unfortunately i do not, i will need to get permission to release it publicly.
@daanx Another concept i had for my situation is that these containers are being run in Kubernetes, which doesn't permit swap. Which really limits what you can do. It would be nice to have an alloc library that you could give physical and virtual memory limits, For example allow 10 gigs of virtual memory but only a total 3 gigs of physical. The library could then fallback to use some memory mapping to disk to make up the 7 gig difference.
The great k8s swap debate https://github.com/kubernetes/kubernetes/issues/53533
Hi, actually the latest dev-slice branch contains the following "experimental" features that can achieve the memory limit. It adds two api entries:
int mi_reserve_os_memory(size_t size, bool commit, bool allow_large);
bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) ;
where you could call mi_reserve_os_memory( size, false, true ) to commit a slice of virtual OS memory that is used now in preference by mimalloc to allocate from. Moreover, you can set the option mi_option_enable(mi_option_limit_os_alloc) which will prevent mimalloc to use the OS directly to satisfy memory request. This will limit allocations to whatever you provide in mi_reserve_os_memory. Ah, and you can invoke both options as an environment option as well, like:
> MIMALLOC_RESERVE_OS_MEMORY=500m MIMALLOC_LIMIT_OS_ALLOC=1 ./myprogram
(reserving like this will always immediately commit the memory though). Let me know if this can work for you.
@daanx Just so i am clear on those settings. Is MIMALLOC_RESERVE_OS_MEMORY actually reserving the large bock ahead of time or just allow me to set the upper bound on how much mimalloc will use?
It reserves a large block ahead of time; but if the commit parameter is false, the memory is just "reserved" as virtual address space so it has no cost (and it is committed on demand). If commit is true, it is immediately committed and reserves swap space for example. (In both cases, the rss (working set) is still unaffected until the memory pages are touched.)
To clarify more, if we just want to limit the amount of OS memory allocated, we can still do something simpler, like keeping track of totally allocated memory and start returning NULL if exhausted. The mi_reseve_os_memory / mi_manage_os_memory are there for other use cases but can also be "abused" for limiting the memory allocated by mimalloc :-)
@daanx Ok i just looked through your changes, i think i have a better understanding, i never really dug too deep into the mimalloc implementation previously, so the 'arena' construct is just mimalloc concept of a block and there is no OS allocation done there. That makes sense and then you are using MIMALLOC_LIMIT_OS_ALLOC so that if that arena is full it will not request anymore memory from the OS.
Should you be setting errno = ENOMEM when you have exhausted the arena and are returning NULL? if (mi_option_is_enabled(mi_option_limit_os_alloc)) return NULL; or is that done at a higher level?
I can give this a try when i get time, it might be a little.
out of curiosity is mimalloc being used at a production level that you know of with Microsoft or elsewhere? Just trying to get a gauge on how well baked it is that environment
Ah, right, errno should be set. Thanks!
Hi @daanx, I have an additional question following the above comment, so if we use mi_reseve_os_memory and mi_option_is_enabled so that if the arena is full then any additional memory requests will fail with ENOMEM, can I then use mi_register_error to register a callback that can perform my custom memory management, where I can choose to either crash or add to the existing arena size as I see fit?