Understanding memory releasing

Hi here,

I'm working on memory usage of one of my program. Its a daemon in embedded GNU/Linux system. So, I need it to not use more memory more than it real need.

I wrote script which make my program work. After this "work", my program should not need more than memory than before this "work". I profiled my program, and I'm pretty sure of that. But, the RSS memory after this "work" is huge (~30Mo before, ~350Mo after).

I read some blog posts about that and seems legitimate. As the memory allocator can keep some memory in case of future need (and improve performance). I'm not familiar with these "low level" things.

But, in my context (embedded), I really need to really free the memory after the "work". What are the usual ways of doing things (specially in Rust) ?

Some evidences:

RSS before :
ApexShot-2026-07-22_11-50-54

And after the "work":
ApexShot-2026-07-22_11-51-49

Memory profile with bytehound:

Thanks in advance !

If you really need it, then you could use a custom global allocator which releases memory more aggressively. Also note that allocators work with page granularity, so depending on you allocation patterns you may end up with one small allocation blocking release of a whole page.

Another option is to spawn a separate process for your memory-heavy task and let OS to reclaim all its memory after it finishes.

Rust automatically and pretty eagerly frees memory to the memory allocator. You can track that with cap crate.

It's up to the allocator to release pages back to the OS. Linux allocators are typically optimized for large desktops and servers, assuming there's plenty of memory and no rush to release.

Another gotcha is memory fragmentation. Memory on the OS level is handled as pages, and if there's any allocation in a page, the whole page must be kept. If you make many allocations and keep some of them, they can keep inflated memory usage. Solution to this is to use memory arenas for groups of objects if you can, reserve space in Vecs upfront. If you're keeping some data after the memory-hungry peak, making a newly allocated copy might help.

You can try with alternative allocators like jemalloc and tcmalloc. They might prevent fragmentation better. They have some config knobs that may make them release memory more aggressively.

Okay, thanks to both of you I will explore that !

If you really need to strictly free memory to the operating system, you must take this into account by using specially developed algorithms and data structures, and move active memory allocations around. This usually means that ordinary references or pointers cannot be used; instead, only integer references, etc., can be used.

The reason for this is that a program can generally only increase or decrease the size of its data segment; it cannot, however, return individual pages to the operating system.

So, if a (small) allocation occupies the last byte of the data segment and there is a large unused address range preceding this allocation, this range cannot be released to the operating system.

In practice, allocators also use anonymously mapped memory, but the consequences are similar.

On nightly you can easier enforce memory limits by using Vec::push_within_capacity.
Although I kinda still miss a extend_within_capacity() which may return the remainder of the iterator in the error case.

Okay I see. According to my "work" structure (Vec with capacity, etc), I guess there is not a lot of fragmentation.

I made a test with malloc_trim call and all expected memory is free. I guess it means I have very low fragmentation.

Another option is to spawn a separate process for your memory-heavy task and let OS to reclaim all its memory after it finishes.

Thread is matching with this sentence ? I use almost all part of my "work" through threads.

Nope, threads share memory space. Exiting a thread only frees its stack and any objects it was holding onto, but you can still have fragmentation problems.

Releasing memory pages eagerly to OS might be necessary in multi-process workspace scenario, but on embedded you'd unlikely run multiple processes concurrently especially heavy ones. Why do you need to really free the memory after work?

Okay. Use real separated process seems problematic in my case as I have a very high data exchange between my "main" process and my "worker".

Yeah, in fact, its not a "real" embedded system. Its a debian system with 2Go RAM and SSD disk, embedded in an industrial machine. Which work without interruption during month or years. And we experience RAM over usage, which trigger EOM kill. Our different programs are almost kill and restart resilient, but, hundreds of unused RAM in our context is annoying.

After saying that, does the OS will release the memory no matter what happens ? Or it can keep it "forever" ?

It would probably be best to mmap a block of memory (with crate like memmap2), then allocate the temporaries from it, and on unmap it will be returned to the OS.

I don't remember what crates allow allocating from &mut [MaybeUninit<u8>], sorry.