24

ho qualche codice javascript che assomiglia a questo:Come forzare JavaScript per copiare in profondità una stringa?

var myClass = { 
    ids: {} 
    myFunc: function(huge_string) { 
    var id = huge_string.substr(0,2); 
    ids[id] = true; 
    } 
} 

In seguito la funzione viene chiamata con alcune stringhe di grandi dimensioni (100 MB +). Voglio solo salvare un ID breve che trovo in ogni stringa. Tuttavia, la funzione di sottostringa di Google Chrome (in realtà regex nel mio codice) restituisce solo un oggetto "stringa a fette", che fa riferimento all'originale. Quindi, dopo una serie di chiamate a myFunc, la mia scheda di Chrome esaurisce la memoria perché gli oggetti temporanei huge_string non possono essere raccolti.

Come è possibile eseguire una copia della stringa id in modo che un riferimento allo huge_string non venga mantenuto e che lo huge_string possa essere sottoposto a garbage collection?

enter image description here

+0

' "" + slice' non sembra funzionare, né' "" + fetta + "" '. Cercando altri approcci. – AffluentOwl

+1

* "la funzione di sottostringa (in realtà regex nel mio codice) restituisce solo un oggetto" stringa a fette ", che fa riferimento all'originale" * - Huh? '.substr()', '.substring()', '.slice()', e le funzioni regex rilevanti restituiscono una * nuova * stringa. L'altro codice che chiama 'myClass.myFunc()' mantiene un riferimento alla tua stringa enorme? Se il tuo codice reale è più complesso, mantiene accidentalmente le stringhe in giro per le chiusure? – nnnnnn

+2

@nnnnnn È impossibile stabilire se si tratta di "nuova" stringa * dati * da JavaScript; le implementazioni * possono * condividere i dati sottostanti senza violare alcuna parte di ECMAScript. Firefox ha una mezza dozzina di [diverse implementazioni di stringhe] (https://blog.mozilla.org/ejpbruel/2012/02/06/how-strings-are-implemented-in-spidermonkey-2/) (vedi JSDependentString in particolare) e non mi sorprende che Chrome abbia ottimizzazioni simili (che potrebbero comportare effetti indesiderati in alcuni casi limite). Detto questo .. Non sarei terribilmente sorpreso se si tratta di un'aringa rossa. – user2864740

risposta

29

implementazione di JavaScript del ECMAScript può variare da browser a browser, ma per Chrome, molte operazioni sulle stringhe (substr, fetta, regex, ecc) semplicemente mantenere i riferimenti alla stringa originale piuttosto che fare le copie del stringa. Questo è un problema noto in Chrome (Bug #2869). Per forzare una copia della stringa, il seguente codice funziona:

var string_copy = (' ' + original_string).slice(1); 

Questo codice funziona aggiungendo uno spazio alla parte anteriore della stringa. Questa concatenazione comporta una copia di stringa nell'implementazione di Chrome. Quindi la sottostringa dopo lo spazio può essere referenziata.

Questo problema con la soluzione è stata ricreata qui: http://jsfiddle.net/ouvv4kbs/1/

ATTENZIONE: richiede molto tempo per caricare, aperta console Chrome debug per vedere un programma progredire.

// We would expect this program to use ~1 MB of memory, however taking 
// a Heap Snapshot will show that this program uses ~100 MB of memory. 
// If the processed data size is increased to ~1 GB, the Chrome tab 
// will crash due to running out of memory. 

function randomString(length) { 
    var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
    var result = ''; 
    for (var i = 0; i < length; i++) { 
    result += 
     alphabet[Math.round(Math.random() * (alphabet.length - 1))]; 
    } 
    return result; 
}; 

var substrings = []; 
var extractSubstring = function(huge_string) { 
    var substring = huge_string.substr(0, 100 * 1000 /* 100 KB */); 
    // Uncommenting this line will force a copy of the string and allow 
    // the unused memory to be garbage collected 
    // substring = (' ' + substring).slice(1); 
    substrings.push(substring); 
}; 

// Process 100 MB of data, but only keep 1 MB. 
for (var i = 0; i < 10; i++) { 
    console.log(10 * (i + 1) + 'MB processed'); 
    var huge_string = randomString(10 * 1000 * 1000 /* 10 MB */); 
    extractSubstring(huge_string); 
} 

// Do something which will keep a reference to substrings around and 
// prevent it from being garbage collected. 
setInterval(function() { 
    var i = Math.round(Math.random() * (substrings.length - 1)); 
    document.body.innerHTML = substrings[i].substr(0, 10); 
}, 2000); 

enter image description here

+0

var string_copy = original_string.slice (0); –

+0

@WesleyStam Penso che il motivo per cui il post di AffluentOwl funziona è che sta anteponendo un carattere alla stringa, il che fa sì che la stringa venga copiata, poiché l'operatore di slice in realtà non copia la stringa come dovrebbe. – NightFantom