[解決済み] Javascriptでガベージコレクタの活動を減らすためのベストプラクティス
質問
私はかなり複雑な Javascript アプリを持っており、1 秒間に 60 回呼び出されるメイン ループを持っています。 多くのガベージ コレクションが行われているようで (Chrome 開発ツールのメモリ タイムラインからの「のこぎり波」出力に基づく)、これはしばしばアプリケーションのパフォーマンスに影響を及ぼします。
そこで、ガベージコレクターがしなければならない仕事の量を減らすためのベスト プラクティスを研究しようとしています。 (Web 上で見つけることができた情報のほとんどは、メモリ リークの回避に関するものでした。これは少し異なる質問ですが、私のメモリは解放されており、ガベージ コレクションがあまりにも多く行われているだけです。) これは主に、ガベージ コレクションが行われることを想定しています。 私は、これは主に、可能な限りオブジェクトを再利用することに起因すると仮定していますが、もちろん、悪魔は細部に宿るものです。
このアプリは次のような「クラス」で構成されています。 John Resig のシンプルな JavaScript 継承 .
私は1つの問題として、いくつかの関数が1秒間に何千回も呼び出されることがあり(メインループの各反復中に何百回も使われるため)、おそらくこれらの関数内のローカル作業変数(文字列、配列など)が問題になるかもしれないと思っています。
私は、より大きな/より重いオブジェクトのためのオブジェクト プールを認識していますが (そして、私たちはこれをある程度使用しています)、全体にわたって適用できるテクニック、特にタイトなループで非常に何度も呼び出される関数に関連するものを探しています。
ガベージコレクターが行わなければならない仕事の量を減らすために、どのようなテクニックを使うことができますか?
そして、おそらくまた - どのオブジェクトが最も多くガベージコレクションされているかを識別するために、どのようなテクニックを採用することができるでしょうか?(非常に大きなコードベースなので、ヒープのスナップショットを比較することはあまり有益ではありません)。
どのように解決するのですか?
GC回転を最小化するために必要なことの多くは、他のほとんどのシナリオで慣用的なJSと考えられていることに反するので、私が与える助言を判断するときは文脈を念頭に置いてください。
最近のインタプリタでは、いくつかの場所で割り振りが行われます。
-
オブジェクトを作成する際に
new
またはリテラル構文で[...]
または{}
. - 文字列を連結するとき。
- 関数宣言を含むスコープに入ったとき。
- 例外を発生させるアクションを実行したとき。
-
関数式を評価したとき。
(function (...) { ... })
. -
のようにObjectに強制するような操作を行った場合。
Object(myNumber)
またはNumber.prototype.toString.call(42)
-
のように、これらのいずれかを行うビルトインを呼び出すと、そのビルトインは自動的に
Array.prototype.slice
. -
を使用する場合
arguments
を使ってパラメータリストの上に反映させることができます。 - 文字列を分割したり、正規表現でマッチするとき。
これらを行わないようにし、可能な限りオブジェクトをプールして再利用しましょう。
具体的には、以下のような機会に気をつけましょう。
- クローズド オーバーの状態への依存がない、または少ない内部関数を、より高い、より長い寿命のスコープに引き出します。 (いくつかのコードミニファイアー、たとえば クロージャコンパイラ のようなコードミニファイラーは内部関数をインライン化することができ、GCパフォーマンスを向上させるかもしれません)。
-
構造化されたデータの表現や動的アドレス指定に文字列を使わないようにしましょう。 特に
split
や正規表現のマッチを使った解析を繰り返すことは避けてください。 これは、ルックアップテーブルのキーや動的な DOM ノード ID でよく起こります。 例えばlookupTable['foo-' + x]
とdocument.getElementById('foo-' + x)
はどちらも文字列の連結があるため、アロケーションが発生します。 多くの場合、再連結する代わりに、長寿命のオブジェクトにキーを添付することができます。 サポートする必要のあるブラウザによりますが、例えばMap
を使用して、オブジェクトを直接キーとして使用することができます。 -
通常のコードパスで例外をキャッチしないようにする。 代わりに
try { op(x) } catch (e) { ... }
の代わりにif (!opCouldFailOn(x)) { op(x); } else { ... }
. -
サーバにメッセージを渡すなど、文字列の作成が避けられない場合は、以下のようなビルトインを使用します。
JSON.stringify
は、複数のオブジェクトを割り当てる代わりに、内部ネイティブバッファを使用してコンテンツを蓄積します。 - 高頻度のイベントにはコールバックの使用を避け、可能な場合は、メッセージの内容から状態を再作成する長寿命の関数(1参照)をコールバックとして渡します。
-
の使用は避ける。
arguments
を使用する関数は、呼び出されたときに配列のようなオブジェクトを作成しなければならないので、これを使用することは避けてください。
を使うことを提案しました。
JSON.stringify
を使って発信するネットワークメッセージを作成することを提案しました。 入力メッセージのパースには
JSON.parse
を使った入力メッセージの解析は明らかに割り当てを必要とし、大きなメッセージの場合は多くの割り当てを必要とします。 もし、入力されるメッセージをプリミティブの配列として表現できれば、多くのアロケーションを節約することができます。 割り当てを行わないパーサーを構築することができる他の組み込み部品は
String.prototype.charCodeAt
. これだけを使用する複雑なフォーマットのパーサーは、読むのが大変になりそうですが。
関連
-
[解決済み] JavaScriptのオブジェクトが空であることをテストするにはどうすればよいですか?
-
[解決済み] JavaScriptで空文字列/未定義文字列/null文字列をチェックするにはどうすればよいですか?
-
[解決済み] JavaScriptでNULL、未定義、空白の変数をチェックする標準的な関数はありますか?
-
[解決済み】JavaScriptの関数にデフォルトのパラメータ値を設定する
-
[解決済み】なぜC++にはガベージコレクタがないのですか?
-
[解決済み] ECMAScriptとは?
-
[解決済み] なぜjavascriptのES6 Promisesはresolve後も実行を継続するのですか?
-
[解決済み] JavaScriptの文字列プリミティブとStringオブジェクトの違いは何ですか?
-
[解決済み] Chrome拡張機能:popup.htmlを強制終了させる
-
[解決済み] 変異を伴わないオブジェクトからの値の削除
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JavaScriptで、ある文字列が別の文字列の中に出現するすべてのインデックスを見つけるにはどうすればよいですか?
-
[解決済み] JavaScriptで:hoverのCSSプロパティを変更する
-
[解決済み] JSXとLoadshを使用して、ある要素をn回繰り返す方法
-
[解決済み] Javascript 空の配列の削減
-
[解決済み] Javascript / jQueryでAndroid端末を検出する。
-
[解決済み] コールバック地獄とは何か、RXはそれをどのように、そしてなぜ解決するのか?
-
[解決済み] moment.jsでミュータビリティを回避するには?
-
[解決済み] これは純関数ですか?
-
[解決済み] JavaScriptデータフォーマット/プリティプリンタ
-
[解決済み] JavaScriptでDIVを表示・非表示にするには?