Risposta breve
Il pop-up "memoria esaurita" dice che stai esaurendo il limite della memoria privata impegnata , un tipo di memoria virtuale. Non che stai esaurendo la RAM (memoria fisica). Non importa quanta RAM disponibile hai. Avere molta RAM disponibile non ti consente di superare il limite di commit. Il limite di commit è la somma della RAM totale (in uso o meno!) Più la dimensione del file di paging corrente.
Al contrario, ciò che "utilizza" il limite di commit (che è principalmente la creazione di spazio di indirizzi virtuali di processo privato) non utilizza necessariamente alcuna RAM! Ma il sistema operativo non consentirà la sua creazione a meno che non sappia che c'è un posto dove memorizzarlo, se necessario. Quindi puoi correre nel limite di commit senza usare tutta la tua RAM, o anche la maggior parte della tua RAM.
Questo è il motivo per cui non si dovrebbe eseguire senza un file di paging. Si noti che il file di paging potrebbe non essere mai stato effettivamente scritto! Ma ti permetterà comunque di evitare gli errori "memoria insufficiente" e "memoria insufficiente".
Risposta intermedia
Windows non ha effettivamente un messaggio di errore per rimanere senza RAM. Quello che stai esaurendo è "limite di commit".
Il grafico "Sistema" in quella versione di Process Explorer è scarsamente chiamato. Dovrebbe essere etichettato "commit charge". (Nella versione che ho si chiama "Impegno di sistema". Meglio, ma ancora non del tutto coerente.) In ogni caso l'altezza "attuale" del grafico c'è ciò che mostra nella parte inferiore del testo come "Commit Charge" - " Corrente "e l'altezza massima del grafico rappresenta" Commit Charge "-" Limit ".
"Commit charge" si riferisce allo spazio di indirizzi virtuale supportato dal file di paging (se ne hai uno) - in altre parole, se non è possibile inserirli tutti nella RAM, il resto va nel file di paging. (Esistono altri tipi di vas che sono supportati da altri file - che si chiama vas "mappati" o che devono rimanere sempre nella RAM; quest'ultimo si chiama "non pagabile".) Il "limite di commit" è il massimo che il "commit charge" può essere. È uguale alla dimensione della RAM più la dimensione del file di paging.
Apparentemente non hai un file di paging (posso dire perché il tuo limite di commit è uguale alla tua dimensione RAM), quindi il limite di commit è semplicemente la dimensione della RAM.
Apparentemente vari programmi + il sistema operativo hanno utilizzato quasi tutto il massimo impegno possibile.
Questo non ha nulla a che fare direttamente con quanta RAM è libera o disponibile. Sì, hai circa 4,5 GB di RAM disponibili. Ciò non significa che puoi superare il limite di commit. La memoria impegnata non utilizza necessariamente la RAM e non è limitata dalla quantità di RAM disponibile.
È necessario riattivare il file di paging - utilizzando questo molto impegnato, suggerirei un file di paging da 16 GB, perché non si desidera forzare il sistema operativo a conservare così tanta roba nella RAM e il file di paging funziona meglio se ha un sacco di spazio libero - altrimenti aggiungi più RAM. Molto più. Per buone prestazioni è necessario disporre di molto spazio nella RAM per il codice e altre cose che non sono supportate dal file di paging (ma possono essere pagate su altri file).
Risposta molto lunga
(ma comunque molto più breve del capitolo sulla gestione della memoria di Windows Internals ...)
Supponiamo che un programma alloca 100 MB di memoria virtuale privata del processo. Questo viene fatto con una chiamata VirtualAlloc con l'opzione "commit". Ciò comporterà un aumento di 100 MB nel "Commit charge". Ma questa "allocazione" in realtà non utilizza alcuna RAM! La RAM viene utilizzata solo quando si accede per la prima volta ad una parte di quello spazio di indirizzi virtuali appena impegnati .
Come viene infine utilizzata la RAM
(se mai lo fa)
Il primo accesso allo spazio appena impegnato sarebbe quasi sempre una scrittura in memoria (leggere un vas privato appena allocato prima di scrivere è quasi sempre un errore di programmazione, poiché i suoi contenuti iniziali sono, a rigor di termini, indefiniti). Ma leggi o scrivi, il risultato, la prima volta che tocchi una pagina di vas appena allocato, è un errore di pagina . Sebbene la parola "errore" suona male, gli errori di pagina sono un evento completamente previsto e persino richiesto in un sistema operativo di memoria virtuale.
In risposta a questo particolare tipo di errore di pagina, il cercapersone (parte del gestore della memoria del sistema operativo, che a volte abbrevierò come "Mm"):
- allocare una pagina fisica di RAM (idealmente dall'elenco delle pagine zero, ma in ogni caso, proviene da ciò che Windows chiama "disponibile": l'elenco delle pagine zero, libero o standby, in quell'ordine di preferenza);
- compilare una voce della tabella delle pagine per associare la pagina fisica alla pagina virtuale; e infine
- chiudere l'eccezione di errore di pagina.
Dopo di che il codice che ha fatto il riferimento di memoria eseguirà nuovamente l'istruzione che ha generato l'errore di pagina e questa volta il riferimento avrà esito positivo.
Diciamo che la pagina è stata "inserita in errore" nel set di lavoro del processo e nella RAM. In Task Manager questo apparirà come un aumento di una pagina (4 KB) nel "set di lavoro privato" del processo. E una riduzione di una pagina nella memoria fisica disponibile. (Quest'ultimo potrebbe essere difficile da notare su una macchina occupata.)
Nota 1: questo errore di pagina non ha comportato alcuna lettura dal disco. Una pagina mai vista prima di memoria virtuale impegnata non inizia la vita sul disco; non ha posto sul disco per leggerlo da . È semplicemente "materializzato" in una pagina di RAM precedentemente disponibile. Statisticamente, infatti, la maggior parte degli errori di pagina vengono risolti nella RAM, sia nelle pagine condivise che sono già nella RAM per altri processi, sia nelle cache delle pagine - gli elenchi di standby o modificati, o come pagine "domanda zero" come questa.
Nota 2: questa operazione richiede solo una pagina, 4096 byte, da "Disponibile". Lo spazio di indirizzi di cui è stato eseguito il commit mai toccato prima viene normalmente realizzato - inserito in errore - solo una pagina alla volta, poiché ogni pagina viene "toccata" per la prima volta. Non ci sarebbe alcun miglioramento, nessun vantaggio, nel fare di più alla volta; ci vorrebbe solo n volte più a lungo. Al contrario, quando le pagine devono essere lette dal disco, viene tentata una certa quantità di "readahead" poiché la maggior parte delle volte in una lettura del disco è in over-over per operazione, non il trasferimento di dati effettivo. L'importo "impegnato" rimane a 100 MB; il fatto che una o più pagine siano state guastate non riduce il costo del commit.
Nota 3:Supponiamo di avere 4 GB di RAM "disponibile". Ciò significa che potremmo fare riferimento alla memoria di commit già allocata ma mai referenziata circa un milione di volte in più (4 GB / 4096) prima di esaurire la RAM. A quel punto, se avessimo un file di paging come previsto da David Cutler e Lou Perazzoli, alcune delle pagine con riferimento a RAM più lunghe fa verrebbero salvate su disco e rese disponibili per l'uso nella risoluzione di questi errori di pagina più recenti. (In realtà il sistema operativo avvierebbe metodi di recupero della RAM come "taglio del set di lavoro" piuttosto prima, e le scritture effettive nel file di paging vengono memorizzate nella cache e memorizzate nell'elenco delle pagine modificate per efficienza, e ...) Nessuna di queste influirebbe sul conteggio "impegnato". È rilevante, tuttavia, per il "limite di commit". Se non c'è spazio per tutti "
E continua a succedere ...
Ma supponiamo di non aver fatto quei milioni di riferimenti in più e che ci siano ancora pagine "disponibili" di circa 4 GB. Supponiamo ora che lo stesso processo - o un altro, non abbia importanza - faccia un altro VirtualAlloc, questa volta diciamo 200 MB impegnati. Ancora una volta, questi 200 MB vengono aggiunti alla commissione di commit e non rimuove alcuna RAM disponibile. Semplicemente lo spazio di indirizzi di VirtualAlloc non utilizza una quantità corrispondente di RAM e avere una RAM "disponibile" bassa non limita la quantità di spazio di indirizzi che è possibile utilizzare VirtualAlloc (né aumentare la RAM disponibile elevata).
(Bene, ok ... c'è un po 'di sovraccarico, pari a una pagina (paginabile!) Che viene utilizzata per una tabella di pagine per ogni 2 MB (4 MB se si utilizza un sistema x86, non PAE) di spazio di indirizzi virtuali allocato e esiste un "descrittore di indirizzi virtuali" di alcune decine di byte per ogni intervallo allocato praticamente contiguo.)
In questo modo è possibile - e comune! - utilizzare un sacco di "commit charge" utilizzando solo piccole quantità di RAM.
Quindi, se "il commit" dello spazio di indirizzi virtuali non utilizza RAM, perché deve esserci un limite?
Perché la "commissione di commit" rappresenta un potenziale uso futuro dello spazio di archiviazione. "Limite di commit" rappresenta la quantità totale di spazio di archiviazione (RAM + spazio file di paging) disponibile per contenere tali allocazioni, nel caso in cui vengano effettivamente referenziati e quindi debba essere archiviato da qualche parte.
Quando Mm approva una richiesta VirtualAlloc, è promettente - "impegnarsi" - che tutti gli accessi di memoria successivi all'area allocata avranno esito positivo; possono causare errori di pagina ma tutti gli errori potranno essere risolti, poiché esiste una memoria adeguata per conservare il contenuto di tutte quelle pagine, sia nella RAM che nel file di paging. Mm lo sa perché sa quanto spazio di archiviazione esiste (il limite di commit) e quanto è già stato "impegnato" (l'attuale addebito di commit).
(Ma a tutte quelle pagine non è stato ancora necessariamente richiesto di accedere, quindi non c'è necessariamente un reale spazio di archiviazione da aggiungere all'importo impegnato, in un dato momento.)
Quindi ... Che dire di "sistema esaurito"?
Se provi a VirtualAlloc e l'attuale commissione di commit più la dimensione di allocazione richiesta ti porterebbe oltre il limite di commit, E il sistema operativo non può espandere il file di paging in modo da aumentare il limite di commit ... otterrai la "memoria insufficiente" pop- su e il processo vede la chiamata di VirtualAlloc FAIL. La maggior parte dei programmi alzerà le mani e morirà a quel punto. Alcuni continueranno a premere alla cieca, supponendo che la chiamata abbia avuto successo e falliranno in seguito quando provano a fare riferimento alla regione che pensavano di aver assegnato.
Ancora una volta (scusate la ripetizione): non importa quanta RAM disponibile avete. Il sistema operativo ha promesso che lo spazio RAM o file di paging sarà disponibile quando è necessario, ma quella promessa non viene sottratta da "Disponibile". La RAM disponibile viene utilizzata da commit vm solo quando viene referenziata per la prima volta, che è ciò che provoca la sua "anomalia" ... cioè realizzata nella memoria fisica. E semplicemente il commit (= allocazione) della memoria virtuale non lo fa. Richiede solo spazio di indirizzi virtuali liberi e ne ricava uno spazio di indirizzi virtuali utilizzabile.
Ma nel caso di "memoria esaurita" c'è stata una richiesta di allocazione per la memoria di commit e il sistema operativo ha aggiunto l'attuale commissione di commit alla dimensione di questa nuova richiesta ... e ha scoperto che il totale è superiore al limite di commit. Quindi se il sistema operativo approvasse questo nuovo, e dopo tutto quello spazio a cui si fa riferimento, non ci sarebbero posti reali (RAM + file di paging) per archiviarlo tutto.
Il sistema operativo non lo consentirà. Non consentirà di assegnare più vas di quanto abbia spazio per tenerlo nel peggiore dei casi - anche se tutto viene "criticato". Questo è lo scopo del "limite di commit".
Te lo dico tre volte te lo dico tre volte te lo dico tre volte: la quantità di RAM "disponibile" non ha importanza. Che lo spazio virtuale impegnato non stia ancora utilizzando tutto quello spazio di archiviazione, non importa. Windows non può "eseguire il commit" per l'allocazione virtuale a meno che non sia possibile "sbagliare" in futuro.
Si noti che esiste un altro tipo di vas chiamato "mappato", utilizzato principalmente per il codice e per l'accesso a file di dati di grandi dimensioni, ma non viene addebitato come "commit commit" e non è limitato dal "limite di commit". Questo perché viene fornito con una propria area di archiviazione, i file che vengono "mappati" su di essa. L'unico limite al vaso "mappato" è la quantità di spazio su disco disponibile per i file mappati e la quantità di vaso libero nel processo in cui mapparli.
Ma quando guardo il sistema, non sono ancora del limite del commit?
Questo è fondamentalmente un problema di misurazione e registrazione. Stai guardando il sistema dopo che una chiamata VirtualAlloc è già stata tentata e fallita.
Supponiamo che siano rimasti solo 500 MB di limite di commit e che alcuni programmi abbiano provato a VirtualAlloc 600 MB. Il tentativo fallisce. Quindi guardi il sistema e dici "Cosa? Restano ancora 500 MB!" In effetti, a quel punto potrebbe esserci ancora molto altro, perché il processo in questione è probabilmente andato completamente a quel punto, quindi TUTTA la sua memoria di commit allocata in precedenza è stata rilasciata.
Il guaio è che non si può guardare indietro nel tempo e vedere che cosa il commit carica era al momento il tentativo è stato fatto alloc. E anche tu non sai di quanto spazio fosse il tentativo. Quindi non puoi vedere definitivamente perché il tentativo non è riuscito o quanto più "limite di commit" sarebbe stato necessario per consentirgli di funzionare.
Ho visto "il sistema sta esaurendo la memoria". Cos'è quello?
Se nel caso precedente il sistema operativo PU expand espandere il file di paging (ovvero lo lasci all'impostazione predefinita "gestita dal sistema", oppure lo gestisci ma imposti il massimo su un valore maggiore di quello iniziale, e c'è abbastanza spazio libero su disco), e tale espansione aumenta sufficientemente il limite di commit per consentire la riuscita della chiamata VirtualAlloc, quindi ... Mm espande il file di paging e la chiamata VirtualAlloc ha esito positivo.
Ed è allora che vedi "il sistema esegue LOW in memoria". Questo è un avvertimento precoce che se le cose continuano senza mitigazione probabilmente vedrai presto un avviso "memoria insufficiente". È ora di chiudere alcune app. Vorrei iniziare con le finestre del browser.
E pensi che sia una buona cosa? L'espansione del file di paging è malvagia !!!
No, non lo è. Vedi, il sistema operativo in realtà non "espande" il file esistente. Alloca solo una nuova misura. L'effetto è molto simile a qualsiasi altro file non contiguo. I vecchi contenuti del file di paging rimangono esattamente dove sono; non devono essere copiati in un nuovo posto o qualcosa del genere. Poiché la maggior parte dei file di paging IO si trova in blocchi relativamente piccoli rispetto alla dimensione del file di paging, le possibilità che un determinato trasferimento attraversi un limite di estensione sono davvero piuttosto rare, quindi la frammentazione non fa molto male a meno che non sia davvero eccessiva.
Infine, una volta terminati tutti i processi che hanno "occupato" spazio nell'estensione (all'arresto del sistema operativo se non prima), le estensioni vengono silenziosamente liberate e il file di paging tornerà alle dimensioni e all'allocazione precedenti - se era contiguo prima, esso è di nuovo così.
Consentire l'espansione del file di paging agisce quindi come una rete di sicurezza completamente gratuita: se lo si consente ma il sistema non ne ha mai bisogno, il sistema non "espanderà e contrarrà costantemente il file di paging" come viene spesso affermato, quindi non costerà nulla . E se mai ne avrai bisogno, ti salverà da crash delle app con errori di "memoria virtuale esaurita".
Ma ma ma ...
Ho letto su decine di siti Web che se si consente l'espansione del file di paging, Windows si espanderà e contrarrà costantemente il file di paging e ciò comporterà la frammentazione del file di paging fino a quando non lo si deframmenta.
Hanno solo torto.
Se non hai mai visto il pop-up "esaurimento memoria" (o, nelle versioni precedenti, "esaurimento memoria virtuale"), il sistema operativo non ha mai espanso il tuo file di paging.
Se vedi quel pop-up, allora questo ti dice che la dimensione del tuo file di paging iniziale è troppo piccola. (Mi piace impostarlo a circa 4 volte l'utilizzo massimo osservato, vale a dire il contatore perfmon "% utilizzo file di paging" dovrebbe essere inferiore al 25%. Motivo: lo spazio del file di paging viene gestito come qualsiasi altro heap e funziona al meglio con molto spazio libero per giocare.)
Ma perché non ...
Si potrebbe sostenere che il sistema operativo dovrebbe solo consentire l'allocazione e quindi lasciare che i riferimenti falliscano se non c'è RAM disponibile per risolvere gli errori di pagina. In altre parole, sopra, dove abbiamo descritto come funziona l'errore di pagina iniziale, cosa succederebbe se la "allocazione di una pagina fisica disponibile di RAM" (passaggio 1) non potesse essere eseguita perché non ce n'era disponibile e non c'era posto lasciato a pagina qualcosa per renderne disponibile?
Quindi il cercapersone non sarebbe in grado di risolvere l'errore di pagina. Dovrebbe consentire all'eccezione (l'errore di pagina) di essere riportata al thread in errore, probabilmente modificata in qualche altro codice di eccezione.
La filosofia di progettazione è che VirtualAlloc restituirà zero (tecnicamente un puntatore NULL) invece di un indirizzo se si esaurisce il limite di commit ed è del tutto ragionevole aspettarsi che il programmatore sappia che una chiamata VirtualAlloc può fallire. Quindi i programmatori dovrebbero verificare quel caso e fare qualcosa di ragionevole in risposta (come darti la possibilità di salvare il tuo lavoro fino a quel punto, e quindi terminare il programma "con grazia"). (Programmatori: verifichi un ritorno del puntatore NULL da malloc, new, ecc., Sì? Allora perché non dovresti farlo?)
Ma i programmatori non dovrebbero aspettarsi che piaccia un semplice riferimento di memoria
i = 0; // initialize loop counter
potrebbe non riuscire, non se si trova in un'area dello spazio degli indirizzi impegnata correttamente. (O lo spazio degli indirizzi mappato, per quella materia). Ma è quello che potrebbe accadere se fosse seguita la filosofia "consenti l'allocomesso allocazione, lascia che il riferimento alla memoria fallisca".
Sfortunatamente, un riferimento di memoria come quello nella riga di codice sopra non ha un modo conveniente per restituire uno stato errato! Dovrebbero solo funzionare , proprio come addizione e sottrazione. L'unico modo per segnalare tali guasti sarebbe come eccezioni. Quindi per gestirli il programmatore dovrebbe racchiudere l'intero programma in un gestore di eccezioni. (prova ... cattura e tutto il resto.)
Ciò può essere fatto ... Ma sarebbe difficile per il gestore sapere come "fare la cosa giusta" in risposta a tali eccezioni, dal momento che ci sarebbero così tanti, molti punti nel codice in cui potrebbero sorgere. (In particolare, potrebbero sorgere ad ogni riferimento di memoria alla memoria di VirtualAlloc'd, alla memoria allocata con malloc o new ... e anche a tutte le variabili locali, poiché anche lo stack è VirtualAlloc'd.)
In breve, rendere il programma fallito con grazia in questi casi sarebbe molto difficile.
È piuttosto facile, d'altra parte, verificare la presenza di un ritorno puntatore NULL da VirtualAlloc (o malloc o nuovo, del resto, anche se non sono esattamente la stessa cosa) e quindi fare qualcosa di ragionevole ... come non provare ad andare e fare qualunque cosa fosse il programma necessario per quello spazio virtuale. E forse chiedere all'utente se desidera salvare il proprio lavoro finora, se presente. (Certo, troppe app non si preoccupano nemmeno di fare così tanto.)
Altri utenti di commit
Per inciso, il "limite di commit" non è ridotto dalle varie allocazioni del sistema operativo come pool paginato e non di paging, elenco PFN, ecc .; questi sono solo incaricati di impegnarsi in carica quando si verificano. Né la commissione di commit o il limite di commit sono influenzati dalla RAM video, né dalla dimensione della "finestra" della RAM video.
Provalo tu stesso
Puoi provare tutto questo con lo strumento testlimit dal sito SysInternals. L'opzione -m assegnerà lo spazio degli indirizzi impegnati ma non lo "toccherà", quindi non causerà l'allocazione della RAM. Considerando che l'opzione -d alloca e fa anche riferimento alle pagine, facendo aumentare sia il commit charge che la RAM disponibile.
Riferimenti
Interni di Windows di Russinovich, Solomon e Ionescu. Ci sono anche dimostrazioni che ti permettono di provare tutti questi punti usando lo strumento testlimit. Tuttavia, devo avvertirvi che se pensate che fosse lungo, state attenti: il solo capitolo Mm è di 200 pagine; quanto sopra è una versione ESTREMAMENTE semplificata. (Dai un'occhiata anche alla sezione "Ringraziamenti" nell'Introduzione.)
Vedi anche la documentazione MSDN VirtualAlloc