2009-10-19 1 views
8

Sto sviluppando un servizio VC++ NT destinato a funzionare ininterrottamente per molti mesi. Utilizza intensivamente l'heap di runtime VC++. Chiaramente la frammentazione dell'heap può a un certo punto causarne il malfunzionamento (pensando che sia fuori dalla memoria).Come rilevare e stimare la frammentazione dell'heap nel mio programma C++?

Quali test posso eseguire sul mio servizio per stimare il grado in cui è soggetta alla frammentazione dell'heap?

risposta

2

Credo che il modo migliore sarebbe scrivere il proprio gestore di memoria (o acquistarne uno) che offre questi dati. Qualsiasi altro modo cambierebbe lo heap stesso e quindi invaliderebbe il risultato.

Una strategia più semplice da implementare consiste nell'allocare blocchi di memoria di dimensioni diverse e attendere un errore, ma non penso che sia un buon modo. Ad ogni modo - più grande era il blocco, che non ha fallito, meno la frammentazione. Ma a seconda del gestore di memoria, l'allocazione del blocco può modificare il risultato.


Modifica: ho trovato un collegamento sull'allocatore di slab (thx per il commento) che mostra le statistiche. È in tedesco e la versione inglese dell'articolo non contiene molte informazioni. Usa babelfish per la traduzione.

http://de.wikipedia.org/wiki/Slab_allocator (babelfish version)

http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf

+0

+1 allocatore lastra. – user7116

+0

Sostituire il gestore della memoria solo per misurarlo è inutile a meno che non si usi quel gestore della memoria in produzione. Penso che una soluzione migliore sarebbe quella di strumentare l'attuale allocatore di memoria e misurare la sua frammentazione. È possibile eseguire questa operazione racchiudendo le chiamate alloc/gratuite oppure è possibile vedere se il gestore della memoria dispone di hook che è possibile utilizzare. –

0

Sono d'accordo con Tobias - rendendo il proprio gestore di memoria è un ottimo modo per fare questo. Conosco solo alcuni sviluppatori di cui mi fiderei per scrivere questo tipo di codice ...

Un'altra possibilità è quella di eseguire la propria raccolta di rifiuti/consolidamento sui propri oggetti di tanto in tanto - a carichi bassi ... cioè il tuo servizio può essere inattivo per un po 'mentre "deframmenta" la memoria che usa, ma non sono sicuro che tu possa garantire il comportamento che vuoi senza la tua gestione della memoria.

5

Hai ricevuto un paio di risposte che parlano di come evitare problemi di frammentazione dell'heap, ma nessuno dei due ha risposto direttamente alla tua domanda. Quasi l'unico modo per stimare la probabilità di problemi di frammentazione è simulare un sacco di utilizzo e misurare la frammentazione che si ottiene.

Poiché si tratta di un servizio NT, la simulazione di mesi di utilizzo consiste principalmente nel fare molte richieste in fretta. È probabile che tu possa effettuare le richieste più velocemente di quanto ci si aspetti normalmente per riceverle, quindi puoi probabilmente simulare diversi mesi di richieste in poche ore, e molto probabilmente anche meno (a seconda della frequenza con cui normalmente ti aspetti di ricevere le richieste).

Dopo aver simulato mesi di lavoro (o anche se lo si sta facendo), è necessario esaminare l'heap per vedere quanta frammentazione si sta ottenendo. Questo non è facile, ma è normalmente possibile. Inizierai iniettando una discussione nel processo del servizio (su Google "iniezione di thread" o qualcosa del genere dovrebbe ottenere una buona quantità di informazioni). Quindi dovrai percorrere l'heap, cercando (in particolare) blocchi liberi, ma troppo piccoli per poter soddisfare la maggior parte delle richieste. Supponendo che tu stia usando MS VC++, tu percorri l'heap con _heapwalk, e percorrerà l'heap indicandoti l'indirizzo, la dimensione e lo stato (libero o in uso) di ciascun blocco nell'heap.

Un ultimo dettaglio: per ottenere risultati significativi, è necessario collegare sia la DLL eseguibile AND contenente il thread inserito nella libreria di runtime in una DLL. Questo significa che ci sarà un solo heap per l'intero processo, quindi il tuo thread iniettato camminerà sullo heap utilizzato dal tuo servizio.Se si collega la libreria standard in modo statico, la DLL e il servizio avranno ciascuno il proprio heap. La DLL eseguirà il proprio heap, che non dirà nulla sullo heap utilizzato dal processo del servizio.

0

Sono sicuro che ci sono strumenti per Windows che possono darti lo stato di una memoria, ma comunque dovresti sviluppare il tuo servizio pensando a questo problema.

Per prima cosa è necessario capire quali sono le allocazioni preformate. Penso che il modo più semplice per farlo sia sovrascrivere gli operatori new ed delete, e da questi nuovi operatori dovresti contare alcune statistiche delle tue allocazioni e quindi chiamare gli operatori new e delete predefiniti del tuo compilatore.

Le statistiche minime che dovresti contare a mio avviso sono il numero di allocazioni delle gamme di blocchi di dimensioni comuni.

ad es. blocchi tra 0 byte a 15 byte, i blocchi tra 16 byte a 32 byte, i blocchi tra 32 byte a 48 byte, ...

È possibile aggiungere anche il numero di assegnazione sequenziale di ciascun intervallo blocchi dimensioni

Dopo aver raccolto questi dati, è possibile ridurre il problema di frammentazione tramite allineando i blocchi alle dimensioni comuni.

La tecnica migliore e semplice per l'allineamento è quello di utilizzare un blocchi che sono potenza di 2.

ad esempio per allineare un numero di vicini numero che dividere per 16, è possibile utilizzare la seguente funzione:

int align(int size) 
{ 
    return ((size + 15) & ~0x0000000F); 
} 

Ovviamente è consigliabile utilizzare le statistiche per selezionare la potenza ottimale di 2 da allineare. L'obiettivo è raggiungere un numero che la maggior parte delle allocazioni raggiungerà in pochi blocchi e nello stesso tempo mantenere ragionevole il sovraccarico dell'allineamento.

Buona fortuna ...

1

Switchin sul mucchio bassa frammentazione per Windows può aiutare a fare il lavoro su sistemi più vecchi. sui nuovi sistemi sue attivata di default (Vista, Server 2008)

HANDLE heaps[1025]; 
    DWORD nheaps = GetProcessHeaps((sizeof(heaps)/sizeof(HANDLE)) - 1, heaps); 
    for (DWORD i = 0; i < nheaps; ++i) { 
    ULONG enableLFH = 2; 
    HeapSetInformation(heaps[i], HeapCompatibilityInformation, &enableLFH, sizeof(enableLFH)); 
    } 

C'è uno strumento VMMap da Sysinternals (ora Microsoft) che dà una buona panoramica sulla frammentazione della memoria.

1

Il modo più semplice per rilevare la frammentazione è determinare l'allocazione più grande che il programma effettuerà e quindi allocare almeno due volte quell'importo ogni tanto. se l'allocazione non riesce cioè restituisce NULL e il vostro utilizzo di heap come determinato dal codice - qualcosa di simile su Windows

PROCESS_MEMORY_COUNTERS counters; 
if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){ 
    result = counters.WorkingSetSize; 
} 

è inferiore a una certa percentuale di memoria di sistema di solito il 75% allora è sicuramente un problema di frammentazione.