This site has been retired. For up to date information, see handbook.gnome.org or gitlab.gnome.org.


[Home] [TitleIndex] [WordIndex

On 2005-04-27 an e-mail conversation occurred between John Richard Moser and Eduardo Pérez Ureta, which is mirrored here. This conversation was about memory allocators and their efficiency, and a redesigned memory allocator independently designed by John. Keep in mind that John is not a well known hacker, so take what he says with a grain of salt ;)

Explaination of The Topic

The problem as seen by John is that the current memory allocators work based on one giant area of memory called the heap, only able to move the higher bound. This means that they hold unused memory from the system if anything below the highest used area is not used. The current allocator from glibc has the following characteristics.

This produces the following effects:

John has theorized that a better design can be made, based on the assumption that one giant heap was useful only until 640KiB of memory was not enough for anything you wanted to do. To this end, the following describes a new allocator.

This has the following effects:

In other words, free space in memory should group itself, and used space in memory should group itself, and large chunks of free space should go back to memory.

It should be noted that glibc's allocator logically splits up the heap and uses different sections for different threads to implement thread safety. In the interest of memory efficiency, it would probably be better to simply use a read-write locking semaphore for the list of miniheaps and for each miniheap, so that fine grained locking can occur. Because pthreads may use malloc(), the allocator may need its own trivial read-write locking semaphore, which means it may need its own mutex code (which is used to build a read-write locking semaphore).

E-Mail Conversation

Eduardo Pérez Ureta wrote:

I saw your reference to using mmap() in malloc() at: http://live.gnome.org/MemoryReduction

I was testing if that was true and using this simple example I saw that in fact glibc libc6_2.3.5-1 from Debian already is using that:

int main (int argc, char *argv[]) {

}

mmap2(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fc8000

Could you be more specific on what is left to be done? And what are the problems with the current implementation?


John Richard Moser wrote:

Right now mmap() is used to get an exclusive allocation for allocations bigger than 128KiB, as you've shown. The problem is that all smaller allocations go into a single mapping (the BSS/Data segment is an mmap() segment in the eyes of the kernel) which only grows up and shrinks down (right side moves):

*****

*****++++

*****++

The current allocators are designed for situations where the right side (higher memory addresses) of the heap to hold data, while there are free areas elsewhere in the heap. They find an area big enough for a requested allocation and use it, growing the heap ONLY when there isn't a big enough area AND the allocation is less than 128KiB.

The problem is that certain usage patterns typical of long-running programs cause spikes in memory usage and then sharp drop-offs, but leave a little bit of data at the end of the heap. For example, a web browser may need 40M of heap space, then grow to about 90 when 15 tabs are open viewing flash sites. As entries are put in the address bar, memory is allocated on the heap to hold the new entries from history. Once all tabs are closed, only about 5-10 MiB of ram may be freed, leaving an 80-90 meg heap that has about 50-60% free space.

For a redesign, I'm trying to work out an allocator that allocates 4MiB mmap() segments and manages those. The following characteristics seem optimal:

If I'm not mistaken, there's three new concepts here: Using miniheaps, favoring the least free allocation blocks (miniheaps), and floating the smaller allocations up to less free blocks when realloc() is used. I believe these would cause the natural arbitrary chunks of contiguous free space to occur in groups large enough to fill miniheaps, thus freeing memory back to the system. It is optimal in terms of memory usage to use smaller miniheaps.

My current code doesn't do all of this; in particular, it misses the last three, and doesn't exclusivate the miniheaps for larger allocations as in the third point. It also segfaults when used, because I suck and coded it all buggy and shit. it runs for a while, but with constant free/alloc it eventually rapes itself. It also appears to push VM space up to the max, something it shouldn't do because I never allocate enough to fill even one miniheap and thus shouldn't be requesting more memory from the system. In short, it's REALLY buggy.


Eduardo Pérez Ureta wrote:

WOW, what a response!!! Let me take some time to digest it ;-) Anyway this should be put in the wiki page: http://live.gnome.org/MemoryReduction if you didn't already did that. As I'm not the only one interested in this topic.


"* Single allocations bigger than the normal "miniheap" size (4MiB) will be adjusted up to use a full, aligned (...)"

Maybe separate mmap allocations should still be done to sizes above 128 KiB (256, 1024, or something). For example, allocating two 3 MiB blocks would give two mmapped areas, but with tracking added.


2024-10-23 11:17