2016-07-28 37 views
5

Einige Programme, an denen ich gerade arbeite, verbrauchen viel mehr Speicher als ich denke, es sollte. Also versuche ich zu verstehen, wie glibc malloc trimming funktioniert. Ich schrieb den folgenden Test:Verstehen glibc malloc trimmen

#include <malloc.h> 
#include <unistd.h> 

#define NUM_CHUNKS 1000000 
#define CHUNCK_SIZE 100 

int main() 
{ 
    // disable fast bins 
    mallopt(M_MXFAST, 0); 

    void** array = (void**)malloc(sizeof(void*) * NUM_CHUNKS); 

    // allocating memory 
    for(unsigned int i = 0; i < NUM_CHUNKS; i++) 
    { 
     array[i] = malloc(CHUNCK_SIZE); 
    } 

    // releasing memory ALMOST all memory 
    for(unsigned int i = 0; i < NUM_CHUNKS - 1 ; i++) 
    { 
     free(array[i]); 
    } 

    // when enabled memory consumption reduces 
    //int ret = malloc_trim(0); 
    //printf("ret=%d\n", ret); 

    malloc_stats(); 

    sleep(100000); 
} 

Testausgang (ohne malloc_trim Aufruf):

Arena 0: 
system bytes  = 112054272 
in use bytes  =  112 
Total (incl. mmap): 
system bytes  = 120057856 
in use bytes  = 8003696 
max mmap regions =   1 
max mmap bytes = 8003584 

Obwohl fast alle Speicher freigegeben wurde, dieser Test Code verbraucht viel mehr residenten Speicher als erwartet:

[[email protected]]# ps aux | grep test 
root  14662 1.8 0.4 129736 **118024** pts/10 S 20:19 0:00 ./test 

Prozess smaps:

0245e000-08f3b000 rw-p 00000000 00:00 0         [heap] 
Size:    109428 kB 
Rss:    109376 kB 
Pss:    109376 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
Private_Clean:   0 kB 
Private_Dirty: 109376 kB 
Referenced:  109376 kB 
Anonymous:  109376 kB 
AnonHugePages:   0 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 
Locked:    0 kB 
VmFlags: rd wr mr mw me ac 
7f1c60720000-7f1c60ec2000 rw-p 00000000 00:00 0 
Size:    7816 kB 
Rss:    7816 kB 
Pss:    7816 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
Private_Clean:   0 kB 
Private_Dirty:  7816 kB 
Referenced:   7816 kB 
Anonymous:   7816 kB 
AnonHugePages:   0 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 
Locked:    0 kB 

Wenn ich ermöglichen den Aufruf den Ausgang des Tests malloc_trim fast gleich bleibt:

ret=1 
Arena 0: 
system bytes  = 112001024 
in use bytes  =  112 
Total (incl. mmap): 
system bytes  = 120004608 
in use bytes  = 8003696 
max mmap regions =   1 
max mmap bytes = 8003584 

jedoch die RSS deutlich verringert:

[[email protected]]# ps aux | grep test 
root  15733 0.6 0.0 129688 **8804** pts/10 S 20:20 0:00 ./test 

Prozess smaps (nach malloc_trim):

01698000-08168000 rw-p 00000000 00:00 0         [heap] 
Size:    109376 kB 
Rss:     8 kB 
Pss:     8 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
Private_Clean:   0 kB 
Private_Dirty:   8 kB 
Referenced:   8 kB 
Anonymous:    8 kB 
AnonHugePages:   0 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 
Locked:    0 kB 
VmFlags: rd wr mr mw me ac 
7f508122a000-7f50819cc000 rw-p 00000000 00:00 0 
Size:    7816 kB 
Rss:    7816 kB 
Pss:    7816 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
Private_Clean:   0 kB 
Private_Dirty:  7816 kB 
Referenced:   7816 kB 
Anonymous:   7816 kB 
AnonHugePages:   0 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 
Locked:    0 kB 

Nach dem Aufruf von malloc_trim wurde der Heap durchsucht. Ich nehme an, dass das 8MB-mmap-Segment immer noch verfügbar ist, weil das letzte Stück Speicher nicht freigegeben wurde.

Warum Heap-Trimmen wird nicht automatisch von malloc durchgeführt? Gibt es eine Möglichkeit, malloc so zu konfigurieren, dass das Trimmen automatisch durchgeführt wird (wenn so viel Speicher gespart werden kann)?

Ich verwende Glibc Version 2.17.

+0

Wenn Sie viel Speicher verwenden und auf besondere Weise behandelt werden müssen, empfehle ich, dies selbst zu tun, indem Sie "mmap" unter POSIX und "VirtualAlloc" unter Windows verwenden. –

Antwort

4

Der Speicher für kleine Zuordnungen stammt aus historischen Gründen hauptsächlich aus einem Pool, der mit dem Systemaufruf brk verwaltet wird. Dies ist ein sehr alter Systemaufruf - mindestens so alt wie Version 6 Unix - und das einzige, was er tun kann, ist Ändern Sie die Größe einer "Arena", deren Position im Speicher fest ist. Das bedeutet, dass der Pool brk nicht über einen Block hinausgehen kann, der noch zugewiesen ist.

Ihr Programm weist N Speicherblöcke zu und gibt dann N-1 von ihnen frei. Der eine Block, den er nicht freigibt, ist derjenige, der sich unter der höchsten Adresse befindet. Das ist das Worst-Case-Szenario für brk: Die Größe kann überhaupt nicht reduziert werden, obwohl 99,99% des Pools ungenutzt ist! Wenn Sie Ihr Programm so ändern, dass der Block, den es nicht freigibt, array[0] anstelle von array[NUM_CHUNKS-1] ist, sollten Sie sehen, dass sowohl der RSS- als auch der Adressraum beim letzten Aufruf von free verkleinert werden.

Wenn Sie explizit malloc_trim aufrufen, versucht es, diese Einschränkung mit einer Linux-Erweiterung madvise(MADV_DONTNEED) umzugehen, die den physischen RAM, aber nicht den Adressraum (wie Sie beobachteten) freigibt. Ich weiß nicht, warum das nur bei einem expliziten Aufruf an malloc_trim geschieht.

Übrigens ist das 8MB mmap-Segment für Ihre anfängliche Zuweisung von array.

+0

Vielen Dank für Ihre Antwort! Ich sah, dass das 'Array' tatsächlich auf dem 8MB-Segment liegt, und ich sah auch, dass' Array [N-1] 'am ** Ende ** des Heap-Segments liegt. Wenn es am Ende ist, verstehe ich jetzt nicht, wie es getrimmt werden kann (beim Aufruf von 'malloc_trim'). – michael

+0

Lesen Sie, was ich über 'madvise' schon wieder gesagt habe ... – zwol

+0

Ich verstehe, danke. Gibt es eine Möglichkeit für mich, die Menge an verbrauchtem RSS-Speicher zu reduzieren? Nebenbei "malloc_trim" rufen Sie hin und wieder? – michael