2011-06-11 4 views
5

Ho lavorato per cercare di capire perché il mio programma sta consumando così tanta RAM di sistema. Sto caricando un file dal disco in un vettore di strutture di diversi array allocati dinamicamente. Un file da 16 MB finisce per consumare 280 MB di RAM di sistema in base al task manager. I tipi nel file sono principalmente caratteri con alcuni corti e alcuni lunghi. Ci sono 331.000 record nel file che contengono in media circa 5 campi. Ho convertito il vettore in una struttura e questo ha ridotto la memoria a circa 255 MB, ma sembra ancora molto alto. Con il vettore che occupa tanta memoria, il programma sta esaurendo la memoria, quindi ho bisogno di trovare un modo per ottenere un uso più ragionevole della memoria.Perché un programma VC++ che memorizza 5 MB di dati consuma 64 MB di memoria di sistema?

Ho scritto un semplice programma per inserire semplicemente un vettore (o array) con 1.000.000 puntatori di caratteri. Mi aspetto di allocare 4 + 1 byte per ognuno dei 5 MB di memoria necessari per l'archiviazione, ma in realtà utilizza 64 MB (versione array) o 67 MB (versione vettoriale). Quando il programma si avvia per la prima volta, consuma solo 400 K, quindi perché è disponibile un ulteriore 59 MB per array o 62 MB per i vettori allocati? Questa memoria extra sembra essere per ogni contenitore, quindi se creo un size_check2 e copio tutto ed eseguo il programma usa 135 MB per 10 MB di indicatori e dati.

Grazie in anticipo,

size_check.h

#pragma once 

#include <vector> 

class size_check 
{ 
public: 
    size_check(void); 
    ~size_check(void); 

    typedef unsigned long size_type; 

    void stuff_me(unsigned int howMany); 

private: 
    size_type**   package; 
// std::vector<size_type*> package; 
    size_type*   me; 
}; 

size_check.cpp

#include "size_check.h" 

size_check::size_check(void) 
{ 
} 

size_check::~size_check(void) 
{ 
} 

void size_check::stuff_me(unsigned int howMany) 
{ 
    package = new size_type*[howMany]; 
    for(unsigned int i = 0; i < howMany; ++i) 
    { 

     size_type *me = new size_type; 
     *me = 33; 
     package[i] = me; 
//  package.push_back(me); 
    } 
} 

main.cpp #include "size_check.h"

int main(int argc, char * argv[ ]) 
{ 
    const unsigned int buckets = 20; 
    const unsigned int size = 50000; 

    size_check* me[buckets]; 

    for(unsigned int i = 0; i < buckets; ++i) 
    { 
     me[i] = new size_check(); 
     me[i]->stuff_me(size); 
    } 
    printf("done.\n"); 
} 
+0

non ho letto il codice in dettaglio, ma tenere a mente che i puntatori occupano spazio troppo. – slartibartfast

+0

È inoltre necessario prendere in considerazione l'allineamento e il riempimento della struttura dei dati. – feathj

+0

@myrkos è corretto - quattro byte, se non ricordo male, su hardware comune. Anche le definizioni della tua classe prendono spazio. Prova a fare un sizeof() di un oggetto size_check che crei, e moltiplicalo per il numero di istanze che crei, e vedi se questo non fa la differenza. –

risposta

3

Nel mio test utilizzando VS2010, una build di debug aveva una dimensione di working set di 52.500 KB. Ma una build di rilascio aveva un set operativo di dimensioni di 20.944 KB.

Le build di debug di solito utilizzano più memoria rispetto alle build ottimizzate a causa del gestore di heap di debug che fa cose come la creazione di memory fences.

Nelle versioni di rilascio, ho il sospetto che il gestore di heap riserva più memoria di quella che si sta effettivamente utilizzando come ottimizzazione delle prestazioni.

+1

@ Mark: quando si fa una domanda e qualcuno risponde, non modificare la risposta per aggiungere i propri commenti; questo è molto confuso. Utilizzare il collegamento Aggiungi commento sotto la risposta a cui si desidera commentare. –

1

Perdita di memoria

package = new size_type[howMany]; // instantiate 50,000 size_type's 
for(unsigned int i = 0; i < howMany; ++i) 
{ 
    size_type *me = new size_type; // Leak: results in an extra 50k size_type's being instantiated 
    *me = 33; 
    package[i] = *me; // Set a non-pointer to what is at the address of pointer "me" 
    // Would package[i] = 33; not suffice? 
} 

Inoltre, assicuratevi di aver compilato in modalità di rilascio

1

Ci potrebbe essere un paio di motivi per cui si sta vedendo una grande impronta di memoria dalla programma di test All'interno della vostra

void size_check::stuff_me(unsigned int howMany) 
{ 

Questo metodo è sempre sempre chiamato con howMany = 50000.

package = new size_type[howMany]; 

Supponendo che questo è su una configurazione a 32 bit la dichiarazione di cui sopra assegnerà 50.000 * 4 byte.

for(unsigned int i = 0; i < howMany; ++i) 
{ 
    size_type *me = new size_type; 

Quanto sopra allocherà nuovo spazio di archiviazione su ciascuna iterazione del ciclo. Dal momento che questo loop 50.000 e l'allocazione non viene mai eliminato che effettivamente occupa altri 50.000 * 4 byte al completamento del ciclo.

 *me = 33; 
     package[i] = *me; 
    } 
} 

Infine, dal momento che stuff_me() viene chiamato 20 volte dal main() vostro programma avrebbe assegnato almeno ~ 8 Mbyte al termine. Se si trova su un sistema a 64 bit, il footprint probabilmente raddoppierà da sizeof(long) == 8 byte.

L'aumento del consumo di memoria potrebbe avere qualcosa a che fare con il modo in cui VS implementa l'allocazione dinamica. Per motivi di prestazioni, è possibile che, a causa delle chiamate multiple a new, il tuo programma riservi memoria extra in modo da evitare di colpire l'OS ogni volta che ne ha bisogno.

FYI, quando ho eseguito il programma di test su mingw-gcc 4.5.2, il consumo di memoria era ~ 20Mbytes - molto più basso di quello che stavi vedendo ma ancora una notevole quantità. Se ho cambiato il metodo stuff_me a questo:

void size_check::stuff_me(unsigned int howMany) 
{ 
    package = new size_type[howMany]; 
    size_type *me = new size_type; 
    for(unsigned int i = 0; i < howMany; ++i) 
    { 
     *me = 33; 
     package[i] = *me; 
    } 
    delete me; 
} 

consumo di memoria scende un po 'fino a ~ 4-5mbytes.

1

Credo di aver trovato la risposta da approfondire la nuova istruzione. Nelle build di debug ci sono due elementi che vengono creati quando si fa un nuovo. Uno è _CrtMemBlockHeader che ha una lunghezza di 32 byte. L'altro è noMansLand (una recinzione di memoria) con una dimensione di 4 byte che ci dà un sovraccarico di 36 byte per ogni nuovo. Nel mio caso, ogni individuo nuovo per un personaggio mi costava 37 byte. Nei build di rilascio, l'utilizzo della memoria è ridotto a circa 1/2, ma non posso dire esattamente quanto viene assegnato per ogni nuovo poiché non riesco ad accedere alla nuova routine/malloc.

Quindi il mio lavoro intorno è quello di allocare un grande blocco di memoria per contenere il file in memoria. Quindi analizza l'immagine della memoria inserendo un vettore di puntatori all'inizio di ciascuno dei record. Quindi, su richiesta, costruisco un record dall'immagine di memoria usando il puntatore all'inizio del record selezionato. In questo modo l'ingombro di memoria è stato ridotto a < 25 MB.

Grazie per tutto il vostro aiuto e suggerimenti.