1. ホーム
  2. javascript

JavaScriptに文字列を強制的にディープコピーさせる方法は?

2023-08-22 07:07:11

質問

私は次のようないくつかのjavascriptのコードを持っています。

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

その後、この関数はいくつかの大きな文字列(100MB以上)で呼び出されます。私は、各文字列で見つけた短い ID だけを保存したいのです。しかし、Google Chrome の部分文字列関数 (私のコードでは実際には正規表現) は、元の文字列を参照する "sliced string" オブジェクトを返すだけです。そのため、一連の myFunc の一連の呼び出しの後、私のクロームタブはメモリ不足になり、一時的な huge_string オブジェクトがガベージコレクションされないためです。

文字列のコピーを作成するには id を参照できるようにするには、どうすればよいでしょうか。 huge_string は維持されず huge_string はガベージコレクションできるのか?

どのように解決するのですか?

JavaScript の ECMAScript の実装はブラウザによって異なりますが、Chrome では、多くの文字列操作 (substr、slice、regex など) が、文字列のコピーを作成するのではなく、単に元の文字列への参照を保持するだけです。これは Chrome の既知の問題です ( バグ番号: #2869 ). 文字列のコピーを強制的に行うには、以下のコードが有効です。

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

このコードは、文字列の前にスペースを追加することで動作します。この連結は、Chromeの実装では文字列のコピーになります。そして、スペースの後の部分文字列を参照することができます。

この問題の解決方法は、こちらで再現されています。 http://jsfiddle.net/ouvv4kbs/1/

警告: 読み込みに長い時間がかかります。Chrome デバッグ コンソールを開いて、進行状況のプリントアウトを確認してください。

// 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);