In base al file node.js, un nodo ha un limite di 512 meg sulla versione a 32 bit e un limite di 1,4 gig sulla versione a 64 bit. I limiti sono simili per Chrome AFAICT. (+/- 25%)Perché in questa situazione v8 esaurisce la memoria?
Quindi, perché questo codice esaurisce la memoria quando non utilizza mai più di ~ 424meg di memoria?
Ecco il codice (Il codice non ha senso. Questa domanda non riguarda ciò che il codice sta facendo, riguarda il motivo per cui il codice non funziona).
var lookup = 'superCaliFragilisticExpialidosiousThispartdoesnotrealllymattersd';
function encode (num) {
return lookup[num];
}
function makeString(uint8) {
var output = '';
for (var i = 0, length = uint8.length; i < length; i += 3) {
var temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
output += encode(temp >> 18 & 0x3F) + encode(temp >> 12 & 0x3F) + encode(temp >> 6 & 0x3F) + encode(temp & 0x3F);
}
return output;
}
function test() {
var big = new Uint8Array(64 * 1024 * 1024 + 2); // multiple of 3
var str = makeString(big);
console.log("big:", big.length);
console.log("str:", str.length);
}
test();
Come si può vedere makeString
costruisce una stringa aggiungendo 4 caratteri alla volta. In questo caso costruirà una stringa 89478988 di lunghezza (180meg) grande. Poiché output
viene aggiunto, l'ultima volta che i caratteri vengono aggiunti, ci saranno 2 stringhe in memoria. Il vecchio con 89478984 caratteri e l'ultimo con 89478988. GC dovrebbe raccogliere qualsiasi altra memoria utilizzata.
Quindi, 64 meg (l'array originale) + 180meg * 2 = 424meg. Bene sotto i limiti del v8.
Ma, se si esegue l'esempio fallirà con la memoria
<--- Last few GCs --->
3992 ms: Scavenge 1397.9 (1458.1) -> 1397.9 (1458.1) MB, 0.2/0 ms (+ 1.5 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep].
4450 ms: Mark-sweep 1397.9 (1458.1) -> 1397.9 (1458.1) MB, 458.0/0 ms (+ 2.9 ms in 2 steps since start of marking, biggest step 1.5 ms) [last resort gc].
4909 ms: Mark-sweep 1397.9 (1458.1) -> 1397.9 (1458.1) MB, 458.7/0 ms [last resort gc].
$ node foo.js
<--- JS stacktrace --->
==== JS stack trace =========================================
Security context: 0x3a8521e3ac1 <JS Object>
2: makeString(aka makeString) [/Users/gregg/src/foo.js:~6] [pc=0x1f83baf53a3b] (this=0x3a852104189 <undefined>,uint8=0x2ce813b51709 <an Uint8Array with map 0x32f492c0a039>)
3: test(aka test) [/Users/gregg/src/foo.js:19] [pc=0x1f83baf4df7a] (this=0x3a852104189 <undefined>)
4: /* anonymous */ [/Users/gregg/src/foo.js:24] [pc=0x1f83baf4d9e5] (this=0x2ce813b...
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Abort trap: 6
provato sia il nodo 4.2.4 e 5.6.0
Quindi, la domanda è perché è esaurendo di memoria?
Alcune cose che ho provato.
Ho provato unendo pezzi
invece di aggiungerlo ai
output
indefinitamente Ho provato controllare se è maggiore di una certa dimensione (come 8k). Se è così, lo metto in un array e ripristina l'output sulla stringa vuota.In tal modo,
output
non è mai più di 8 k grande. L'array contiene 180meg + contabilità. Quindi 180meg + 8k è molto meno di 180meg + 180meg. Funziona ancora a corto di memoria. Ora, alla fine di quel processo, io unisco l'array, a quel punto userà più memoria (180meg + 180meg + bookeeping). Ma, v8 si blocca prima che arrivi alla linea .Ho provato a cambiare la codifica a poco
function encode(num) { return 'X'; }
In questo caso si esegue effettivamente a compimento !! Così ho pensato, "A-ha! la questione deve essere qualcosa legato al
lookup[num]
generare una nuova stringa a tutte le chiamate? Così ho provato ...Modificato
lookup
di array di stringhevar lookup = Array.prototype.map.call( 'superCaliFragilisticExpialidosiousThispartdoesnotrealllymattersd', function(c) { return c; });
Ancora esaurito la memoria
Questo sembra un bug in v8?Non è in grado di interpretare le stringhe inutilizzate di GC in un modo strano a causa di questo codice, sebbene il # 2 rispetto al # 3 sia strano in quanto sembrano equivalenti in termini di utilizzo della memoria.
Perché in queste situazioni la memoria è esaurita? (e c'è una soluzione alternativa)
Molti collezionisti di rifiuti * congelano il mondo * quando sono fuori memoria e recuperano qualsiasi memoria possibile da recuperare. Vedi la risposta accettata per il motivo per cui ha esaurito la memoria se sei curioso – gman