Mallocs di 64 bit

Quali sono le ragioni per cui un malloc () fallirebbe, specialmente a 64 bit?

Il mio problema specifico è cercare di mallocare un enorme blocco da 10 GB di RAM su un sistema a 64 bit. La macchina ha 12 GB di RAM e 32 GB di swap. Sì, il malloc è estremo, ma perché sarebbe un problema? Questo è in Windows XP64 con i compilatori Intel e MSFT. Talvolta il malloc ha successo, a volte no, circa il 50%. I mallocs da 8 GB funzionano sempre, i mallocs da 20 GB falliscono sempre. Se un malloc fallisce, le richieste ripetute non funzioneranno, a meno che non esca dal processo e ricomincio un nuovo processo (che avrà quindi il 50% di risultati positivi). Non ci sono altre grandi app in esecuzione. Succede anche immediatamente dopo un nuovo riavvio.

Potrei immaginare un malloc che fallisce a 32 bit se hai esaurito i 32 (o 31) bit di spazio di indirizzamento disponibili, in modo tale che non ci sia un intervallo di indirizzi abbastanza grande da poter essere assegnato alla tua richiesta.

Potrei anche immaginare un errore di malloc se hai esaurito la RAM fisica e lo spazio di swap del disco rigido. Questo non è il mio caso.

Ma perché altro potrebbe fallire un malloc? Non riesco a pensare ad altri motivi.

Sono più interessato alla domanda malloc generale che al mio esempio specifico, che probabilmente sostituirò comunque con file mappati in memoria. Il malloc () fallito è solo più un enigma di qualsiasi altra cosa … il desiderio di capire i tuoi strumenti e non essere sorpreso dai fondamenti.

malloc tenta di allocare un intervallo di memoria contiguo, e questo sarà inizialmente nella memoria reale semplicemente a causa di come funziona la memoria di scambio (almeno per quanto mi ricordo). Potrebbe facilmente accadere che il tuo sistema operativo a volte non riesca a trovare un blocco contiguo di 10 GB di memoria e lasci comunque tutti i processi che richiedono la memoria reale nella RAM contemporaneamente (a quel punto il tuo malloc fallirà).

In realtà, hai bisogno di 10 GB di memoria contigua, oppure saresti in grado di avvolgere una class di memoria / struttura attorno a diversi blocchi più piccoli e utilizzare invece la tua memoria in blocchi? Questo allenta l’enorme fabbisogno contiguo e dovrebbe anche consentire al programma di utilizzare il file di scambio per i blocchi meno utilizzati.

Hai provato a utilizzare VirtualAlloc() e VirtualFree() direttamente? Questo può aiutare a isolare il problema.

  • Sarai bypassare l’heap del runtime C e l’heap NT.
  • È ansible prenotare lo spazio degli indirizzi virtuali e quindi eseguirne il commit. Questo ti dirà quale operazione fallisce.

Se la prenotazione dello spazio degli indirizzi virtuali non riesce (anche se non dovrebbe, a giudicare da quello che hai detto), VMMap di Sysinternals può aiutare a spiegare perché. Attiva “Mostra regioni libere” per vedere come lo spazio di indirizzi virtuale libero è frammentato.

Solo una supposizione qui, ma malloc alloca memoria contigua e potresti non avere una sezione contigua sufficientemente grande sul tuo heap. Ecco alcune cose che vorrei provare;

Dove fallisce un malloc da 20 GB, riescono quattro malloc da 5 GB? Se è così, è un problema di spazio contiguo.

Hai controllato le opzioni del tuo compilatore per tutto ciò che limita la dimensione totale dell’heap o le dimensioni del blocco heap più grandi?

Hai provato a scrivere un programma che dichiari una variabile statica della dimensione richiesta? Se funziona, puoi implementare il tuo heap con mallocs grandi in quello spazio.

Hai provato a usare le funzioni heap per allocare la tua memoria?

Ecco una fonte ufficiale che stabilisce che la dimensione massima della richiesta dell’heap è definita dalla libreria CRT collegata (a parte il codice precedente che ha overflow di interi che vanno a 0, motivo per cui non è stato restituito NULL) (_HEAP_MAXREQ).

http://msdn.microsoft.com/en-us/library/6ewkz86d.aspx

Controlla qui la mia risposta per le grandi allocazioni di Windows, includo un riferimento a un documento MS sui progressi del modello di memoria Vista / 2008.

In breve, il CRT di serie non supporta, anche per un processo nativo a 64 bit qualsiasi dimensione di heap maggiore di 4 GB. Devi usare VirtualAlloc * o CreateFileMapping o altri analoghi.

Oh ho anche notato che stai sostenendo che le tue allocazioni più grandi sono effettivamente riuscite, questo è in realtà errato, stai interpretando male il malloc (0x200000000); (che è 8 gb in esadecimale), ciò che sta accadendo è che stai richiedendo un’assegnazione di 0 byte a causa di un cast o qualche altro effetto del tuo harness test, non stai assolutamente osservando qualcosa di più grande di un heap 0xfffff000 che viene commesso, è semplicemente stai vedendo l’intero traboccare verso il basso.

PAROLA AL WYSE o * SUGGERIMENTI PER SALVARE IL TUO SANNO DEL CUORE *

L’ UNICO MODO PER ASSEGNARE LA MEMORIA CON MALLOC (O QUALSIASI ALTRA RICHIESTA DINAMICA)

 void *foo = malloc(SIZE); 

IL VALORE DI UNA RICHIESTA DI MEMORIA DINAMICA NON DEVE MAI (NON SAREBBE STRESSATO CHE ABBASTANZA) ESSERE CALCOLATO NELL’AMBITO DEL “()” PAREN’S DELLA RICHIESTA

 mytype *foo = (mytype *) malloc(sizeof(mytype) * 2); 

Il pericolo è che si verifichi un overflow dei numeri interi .

È sempre un ERRORE di codifica per eseguire l’aritmetica al momento della chiamata, È SEMPRE OBBLIGATORIO calcolare la SOMMA TOTALE dei dati da richiedere prima dell’istruzione che valuta la richiesta.

perché è così cattivo? Sappiamo che questo è un errore, perché il punto in cui viene fatta una richiesta per le risorse dinamiche, ci deve essere un punto nel futuro in cui useremo questa risorsa.

Per utilizzare ciò che abbiamo richiesto, dobbiamo sapere quanto è grande? (ad esempio il numero di array, la dimensione del tipo, ecc.).

Ciò significherebbe, se mai vedessimo qualsiasi aritmetica, all’interno della () di una richiesta di risorsa, si tratta di un errore, poiché DEVE duplicare nuovamente quel codice per poter utilizzare in modo appropriato tali dati.

Il problema è che Visual Studio non definisce WIN64 quando si compila un’applicazione a 64 bit, di solito mantiene WIN32, il che è sbagliato per le app a 64 bit. In questo modo, il tempo di esecuzione viene utilizzato per utilizzare il valore a 32 bit quando _HEAP_MAXREQ è definito, quindi tutti i grandi malloc() avranno esito negativo. Se si modifica il progetto (sotto le proprietà del progetto, le definizioni preelaborate) in WIN64, allora il malloc() molto grande non dovrebbe presentare alcun problema.

Ma perché altro potrebbe fallire un malloc? Non riesco a pensare ad altri motivi

Come implicitamente affermato in precedenza più volte, a causa della frammentazione della memoria

Ho trovato la domanda interessante, quindi ho provato a ricercarla, da un punto di vista teorico:

A 64-bit (effettivamente utilizzabili a 48 bit a causa delle limitazioni dei chip e meno (44 bit?) A causa delle limitazioni del sistema operativo) non dovresti certo essere limitato dalla frammentazione della memoria virtuale, ovvero dalla mancanza di spazio di indirizzamento virtuale contiguo. La ragione è che c’è così tanto spazio di indirizzamento virtuale che è piuttosto poco pratico esaurirlo.

Inoltre, possiamo aspettarci che la frammentazione della memoria fisica non dovrebbe essere un problema, poiché la memoria virtuale significa che non è necessario che sia un intervallo di indirizzi di memoria fisica contigua per soddisfare una richiesta di allocazione. Invece può essere soddisfatto con qualsiasi set di pagine di memoria sufficientemente grande.

Quindi devi imbatterti in qualcos’altro : oe qualche altra limitazione che si applica alla memoria virtuale.

Un altro limite che sicuramente esiste su Windows è il limite di commit. Maggiori informazioni su questo:

http://blogs.technet.com/b/markrussinovich/archive/2008/11/17/3155406.aspx

Potrebbero esistere altri limiti possibili, ad esempio il modo in cui l’implementazione effettiva deve funzionare con l’hardware effettivo. Immagina che quando provi a creare una mapping dello spazio di indirizzamento virtuale nello spazio dell’indirizzo fisico, le voci nella tabella delle pagine finiscano per eseguire la mapping degli indirizzi virtuali … il codice degli allocatori di memoria del sistema operativo si preoccupa di gestire questo scenario improbabile? Forse no…

Puoi leggere ulteriori informazioni su come le tabelle di pagina effettivamente funzionano per eseguire la traduzione degli indirizzi virtuali qui:

http://en.wikipedia.org/wiki/Memory_management_unit

È molto probabile la frammentazione. Per semplicità, usiamo un esempio.

La memoria consiste in un singolo modulo da 12kb. Questa memoria è organizzata in blocchi da 1kb nella MMU. Quindi, hai 12 blocchi da 1kb. Il tuo sistema operativo utilizza 100 byte ma questo è fondamentalmente il codice che gestisce le tabelle delle pagine. Quindi, non puoi scambiarlo. Quindi, tutte le tue app utilizzano 100 byte ciascuna.

Ora, con solo il tuo sistema operativo e la tua applicazione in esecuzione (200 byte), utilizzeresti già 200 byte di memoria (occupando blocchi da 2kb). Lasciando esattamente 10kb disponibile per malloc() .

Ora, hai avviato con malloc() un paio di buffer: A (900 byte), B (200 byte). Quindi, si libera A. Ora, si ha 9.8kb gratuiti (non contigui). Quindi, provi a malloc() C (9kb). Improvvisamente, fallisci.

Hai 8,8 k contiguo alla fine della coda e 0,9 k alla fine anteriore. Non puoi ri-mappare il primo blocco fino alla fine perché B si estende sul primo 1k e sul secondo blocco di 1k.

Puoi ancora malloc() un singolo blocco da 8kb.

Certo, questo esempio è un po ‘inventato, ma spero che aiuti.