翻訳者のメモ:私は外国語を翻訳したのは初めてで、私の言葉は必然的に少しあいまいですが、著者の当初の意図を表現するために最善を尽くし、ポーランド語はあまりありませんでした。批判と修正は大歓迎です。さらに、この記事は長く、大量の情報があり、消化するのが難しい場合があります。詳細について話し合うためにメッセージを残してください。この記事では、主にV8のパフォーマンスの最適化に焦点を当てており、一部のコンテンツはすべてのJSエンジンには適用できません。最後に、転載時にソースを示してください:)
=========================================================================
GoogleのV8エンジン(ChromeおよびNodeが使用)などの多くのJavaScriptエンジンは、迅速な実行を必要とする大規模なJavaScriptアプリケーション向けに特別に設計されています。開発者であり、メモリの使用とページのパフォーマンスに関心がある場合は、ブラウザのJavaScriptエンジンがどのように機能するかを理解する必要があります。 V8、Spidermonkey(Firefox)Carakan(Opera)、Chakra(IE)、または他のエンジンであろうと、そうすることで、アプリケーションをよりよく最適化するのに役立ちます。これは、特定のブラウザやエンジンに特に最適化する必要があるという意味ではなく、これを行わないことを意味します。
ただし、いくつかの質問を自問する必要があります。
高速ロードウェブサイトは、特別にカスタマイズされた部品を必要とする高速スポーツカーのようなものです。画像ソース:dhybridcars。
高性能コードを書くときにいくつかの一般的な落とし穴があります。この記事では、コードを書くためのいくつかの実証済みの方法を示します。
JSエンジンを深く理解していない場合、運転できる人がフードしか見ていないように、大規模なWebアプリケーションの開発に問題はありません。 Chromeが私のブラウザの最初の選択肢であることを考えると、そのJavaScriptエンジンについて話しましょう。 V8は、次のコアパーツで構成されています。
ガベージコレクションはメモリ管理の一形態であり、実際にはコレクターの概念であり、使用されなくなったオブジェクトで占められているメモリをリサイクルしようとしています。 JavaScriptのようなごみ収集言語では、アプリケーションでまだ参照されているオブジェクトはクリアされません。
ほとんどの場合、オブジェクトの参照を手動で排除する必要はありません。必要な場所に変数を配置するだけで、すべてが正常に機能します(理想的には、可能な限り局所的にスコープされたもの、つまり、機能の外層の代わりに使用される関数)。
ゴミコレクターはメモリをリサイクルしようとします。画像ソース:ValtteriMäki。
JavaScriptでは、ゴミ収集を強制することは不可能です。ゴミ収集プロセスはランタイムによって制御され、クリーンアップするのに最適な時期がわかっているため、これを行うべきではありません。
キーワード削除に関するインターネット上でJavaScriptメモリリサイクルについて多くの議論があります。オブジェクト(MAP)の属性(キー)を削除するために使用できますが、一部の開発者は、「参照」を強制するために使用できると考えています。可能な限り削除を使用しないようにすることをお勧めします。以下の例ではdelete ox 的弊大于利,因为它改变了o的隐藏类,并使它成为一个"慢对象"。
var o = {x:1}; OXを削除します。 // True Ox; // 未定義人気のあるJSライブラリで参照削除を簡単に見つけることができます - これは言語的に目的です。ここでは、実行時に「ホット」オブジェクトの構造を変更しないようにする必要があります。 JavaScriptエンジンは、このような「ホット」オブジェクトを検出し、それらを最適化しようとすることができます。ライフサイクル中にオブジェクトの構造が大幅に変化しない場合、エンジンはオブジェクトを最適化するのが簡単になり、削除操作は実際にこの大きな構造変化を引き起こします。これはエンジンの最適化を助長しません。
ヌルの仕組みについても誤解があります。 NULLにオブジェクト参照を設定しても、オブジェクトが「空」になるのではなく、その参照を空に設定するだけです。 ox = nullの使用は、削除を使用するよりも優れていますが、必要ない場合があります。
var o = {x:1}; o = null; o; // nullo.x // typeRerrこの参照が現在のオブジェクトへの最後の参照である場合、オブジェクトはゴミ収集されます。この参照が現在のオブジェクトへの最後の参照ではない場合、オブジェクトはアクセス可能であり、ゴミ収集されません。
また、ページのライフサイクル中に、グローバル変数はガベージコレクターによってクリーンアップされないことに注意する必要があります。ページがどれだけ長く開いていても、JavaScriptが実行されると、グローバルオブジェクトの範囲内の変数が常に存在します。
var myglobalnamespace = {};グローバルオブジェクトは、ページを更新したり、別のページに移動したり、タブを閉じたり、ブラウザを終了したりするときにのみクリーニングされます。関数スコープの変数は、範囲外の場合、つまり関数が終了すると参照がなく、そのような変数がクリーニングされます。
ゴミコレクターができるだけ多くのオブジェクトをできるだけ多くのオブジェクトを収集するためには、使用されなくなったオブジェクトを保持しないでください。ここに覚えておくべきことがいくつかあります:
次に、機能について話しましょう。すでに述べたように、ごみ収集は、もはやアクセスできないメモリ(オブジェクト)をリサイクルすることで機能します。これをよりよく説明するために、いくつかの例を紹介します。
function foo(){var bar = new lagerObject(); bar.somecall();}Fooが戻ってくると、既存の参照がないため、Barで指されたオブジェクトはGarbage Collectorによって自動的にリサイクルされます。
比較する:
function foo(){var bar = new lagerObject(); bar.somecall(); return bar;} //どこか他の場所b = foo();これで、バーオブジェクトを指す基準があり、バーオブジェクトのライフサイクルは、発信者が別の変数B(またはBが範囲外)を指定するまで、FOOへの呼び出しから継続します。
関数が表示されたら、外部関数が実行された後でも、スコープアクセスから抜け出す内部関数を返します。これは基本的な閉鎖です - 特定のコンテキストで設定できる変数の式。例えば:
function sum(x){function sumit(y){return x + y; }; return sumit;} // usagevar suma = sum(4); var sumb = suma(3); console.log(sumb); // 7を返します合計コールコンテキストで生成された関数オブジェクト(SUMIT)をリサイクルできません。グローバル変数(SUMA)によって参照され、SUMA(n)を介して呼び出すことができます。
別の例を見てみましょう。どこで可変最大gestrにアクセスできますか?
var a = function(){var mastargestr = new Array(1000000).Join( 'x'); return function(){return ragastrr; };}();はい、a()を介して最大gastRにアクセスできるため、リサイクルされません。次のことはどうですか?
var a = function(){var smallstr = 'x'; var ragastR = new Array(1000000).Join( 'x'); return function(n){return smallstr; };}();私たちはもはや最大のアクセスにはアクセスできません。それはすでにゴミ収集候補です。 [翻訳者のメモ:最大の参照がなくなったため]
メモリをリークする最悪の場所の1つは、ループまたはsetimeout()/setInterval()ですが、これは非常に一般的です。次の例について考えてください。
var myobj = {callmemaybe:function(){var myref = this; var val = setimeout(function(){console.log( 'time is dunnow!'); myref.callmemaybe();}、1000); }}; myobj.callmemaybe()を実行した場合;タイマーを開始するために、コンソールが「時間がなくなっています!」と印刷することがわかります。毎秒。 myObj = null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。
また、SettimeOut/SetInterval呼び出し(関数など)の参照は、ガベージを収集する前に実行および完了する必要があることを覚えておく価値があります。
本当に必要になるまでコードを最適化しないでください。これで、NがMよりもV8で最適化されていることを示すいくつかのベンチマークを見ることができますが、モジュールコードまたはアプリケーションでテストすると、これらの最適化は予想よりもはるかに小さいことがわかります。
あまりにも多くのことをするよりも何もする方が良いです。画像ソース:Tim Sheerman-Chase。
たとえば、このようなモジュールを作成したい:
この問題にはいくつかの異なる要因がありますが、解決も簡単です。データをどのように保存し、どのようにテーブルを効率的に描画してDOMに追加するのか、テーブルイベントをより適切に処理する方法をどのように処理しますか?
これらの問題に直面する最初の(ナイーブな)アプローチは、オブジェクトを使用してデータを保存して配列に配置し、jQueryを使用してデータをトラバースしてテーブルを描画し、DOMに追加し、最終的に予想されるクリック動作にイベントバインディングを使用することです。
注:これはあなたがすべきことではありません
var modulea = function(){return {data:dataarrayobject、init:function(){this.addtable(); this.addevents(); }、addtable:function(){for(var i = 0; i <rows; i ++){$ tr = $( '<tr> </tr>'); for(var j = 0; j <this.data.length; j ++){$ tr.append( '<td>' + this.data [j] ['id'] + '</td>'); } $ tr.appendto($ tbody); }}、addevents:function(){$( 'table td')。 }};}();このコードは、タスクを簡単かつ効果的に完了します。
しかし、この場合、私たちが通過するデータは、単に配列に保存されるはずの数値プロパティIDにすぎません。興味深いことに、jQueryを使用してテーブルを生成するよりも直接DocumentFragmentとローカルDOMメソッドを使用する方が良いです(このように)、もちろん、イベントプロキシは各TDのみを結合するよりも高いパフォーマンスを持っています。
jQueryは内部的にドキュメントフラグメントを使用していますが、この例では、コード呼び出しがループ内に追加されており、これらの呼び出しには他のわずかな知識が含まれているため、ここでの最適化効果はあまり良くありません。うまくいけば、これが問題のポイントにならないことを願っていますが、ベンチマークを行って、コードが問題ないことを確認してください。
この例では、上記のプラクティスは(望ましい)パフォーマンスの改善をもたらします。イベントのプロキシは、単純なバインディングの改善であり、オプションのドキュメントフラグメントも役立ちます。
var moduled = function(){return {data:dataArray、init:function(){this.addtable(); this.addevents(); }、addtable:function(){var td、tr; var fragment = document.createdocumentfragment(); var fragment2 = document.createdocumentfragment(); for(var i = 0; i <rows; i ++){tr = document.createelment( 'tr'); for(var j = 0; j <this.data.length; j ++){td = document.createelement( 'td'); TD.AppendChild(document.createTextNode(this.data [j])); frag2.AppendChild(TD); } tr.AppendChild(frag2); frag.AppendChild(TR); } tbody.appendChild(frag); }、addevents:function(){$( 'table')。 }};}();パフォーマンスを向上させる他の方法を見てみましょう。プロトタイプモードを使用することはモジュールモードよりも優れているか、JSテンプレートフレームワークを使用するとパフォーマンスが向上することを聞いたことがあるかもしれません。これは真実であることもありますが、コードをより読みやすくするために使用されます。ちなみに、事前コンパイルがあります!それが実際にどのように機能するか見てみましょうか?
moduleg = function(){}; moduleg.prototype.data = dataArray; moduleg.prototype.init = function(){this.addtable(); this.addevents();}; moduleg.prototype.addtable = function(){var template = _.template($( '#template')。text()); var html = template({'data':this.data}); $ tbody.append(html);}; moduleg.prototype.addevents = function(){$( 'table')。この状況にもたらされるパフォーマンスの改善は無視できることがわかります。テンプレートとプロトタイプの選択は、それ以上のものを提供するものではありません。つまり、パフォーマンスが開発者がそれらを使用する理由ではなく、読みやすさ、継承モデル、およびコードにもたらされる保守性が実際の理由です。
より複雑な問題には、キャンバスに写真を効率的に描画し、タイプの配列の有無にかかわらずピクセルデータを操作することが含まれます。
独自のアプリケーションにいくつかの方法を使用する前に、これらのソリューションのベンチマークについて詳しく知るようにしてください。たぶん、誰かがまだJSテンプレートの撮影とその後の拡張を覚えているかもしれません。確認できない仮想アプリケーションにはベンチマークが存在しないが、実際のコードによってもたらされた最適化をテストする必要があることを理解する必要があります。
各V8エンジンの最適化ポイントは、この記事の範囲外で詳細に導入されています。もちろん、ここに言及する価値のあるヒントはたくさんあります。これらのヒントを忘れないでください。パフォーマンスが低いコードを減らすことができます。
関数add(x、y){return x+y;} add(1、2); add( 'a'、 'b'); add(my_custom_object、undefined);その他のコンテンツについては、Google I/OでのDaniel Cliffordの共有をご覧ください。 V8でJavaScript速度制限を破ります。 V8の最適化 - シリーズも読む価値があります。
JavaScriptのオブジェクトと配列の間には、アレイの魔法の長さのプロパティには1つの主な違いがあります。このプロパティを自分で維持すると、V8のオブジェクトと配列は配列のオブジェクトと同じくらい速くなります。
オブジェクトのクローニングは、アプリケーション開発者にとって一般的な問題です。さまざまなベンチマークでは、V8がこの問題をうまく処理することを証明できますが、注意してください。大きなことをコピーすることは通常遅くなります - それをしないでください。 for .. in loop in jsは、悪魔の仕様があり、エンジンのどのオブジェクトよりも速くないため、特に悪いです。
クリティカルパフォーマンスコードパスでオブジェクトをコピーする場合は、配列またはカスタム「コピーコンストラクター」関数を使用して、各プロパティを明示的にコピーします。これはおそらく最速の方法です。
function clone(original){this.foo = original.foo; this.bar = original.bar;} var copy = new clone(original);モジュールモードを使用する場合のキャッシュ機能は、パフォーマンスの改善につながる可能性があります。以下の例を参照してください。常にメンバー関数の新しいコピーを作成するため、表示される変更は遅くなる可能性があります。
また、プロトタイプモードに依存するだけでなく、この方法を使用することは明らかに優れていることに注意してください(JSPERFテストで確認)。
モジュールモードまたはプロトタイプモードを使用する場合のパフォーマンスの改善
これは、プロトタイプモードとモジュールモードのパフォーマンス比較テストです。
//プロトタイプパターンklass1 = function(){} klass1.prototype.foo = function(){log( 'foo'); } klass1.prototype.bar = function(){log( 'bar'); } //モジュールパターンklass2 = function(){var foo = function(){log( 'foo'); }、bar = function(){log( 'bar'); }; return {foo:foo、bar:bar}} // cached関数を備えたモジュールパターンvar foofunction = function(){log( 'foo'); }; var barfunction = function(){log( 'bar'); }; klass3 = function(){return {foo:foofunction、bar:barfunction}} // iteration tests // prototypal var i = 1000、objs = []; while(i-){var o = new Klass1()objs.push(new Klass1()); O.BAR; o.foo; } //モジュールパターンvar i = 1000、objs = []; while(i-){var o = new Klass1()objs.push(new Klass1()); O.BAR; o.foo; } //モジュールパターンvar i = 1000、objs = []; while(i-){var o = klass2()objs.push(klass2()); O.BAR; o.foo; } //キャッシュされた関数を使用したモジュールパターンvar i = 1000、objs = []; while(i-){var o = klass3()objs.push(klass3()); O.BAR; o.foo; } //詳細については、テストを参照してください次に、配列に関連するテクニックについて話しましょう。一般に、配列要素を削除しないでください。これにより、アレイがより遅い内部表現に移行します。インデックスがスパースになると、V8は要素をより遅い辞書パターンに変えます。
アレイリテラルは非常に便利であり、VMアレイのサイズとタイプを示唆することができます。通常、小さなサイズのアレイで使用されます。
//ここでV8は、番号を含む4要素アレイが必要であることがわかります:var a = [1、2、3、4]; //これをしないでください:a = []; //ここでv8はarrayforについて何も知りません(var i = 1; i <= 4; i ++){a.push(i);}ミックスタイプ(数字、文字列、未定義、真/偽)のデータを配列に保存することは決して良い考えではありません。たとえば、var arr = [1、“ 1”、未定義、真、「真」]
タイプ推論のパフォーマンステスト
これまで見てきたように、整数の配列は最速です。
スパースアレイを使用する場合、要素にアクセスするように注意してください。フル配列よりもはるかに遅くなります。 V8は、スペースの一部のみを使用するアレイにスペース全体を割り当てないためです。代わりに、辞書で管理されており、両方のスペースを保存しますが、アクセスに時間がかかります。
スパースアレイとフル配列のテスト
最大サイズの大きなアレイ(64Kを超える要素など)を事前に割り当てないでくださいが、動的に割り当てる必要があります。この記事でパフォーマンスをテストする前に、これは一部のJavaScriptエンジンにのみ適用されることを忘れないでください。
空のリテラルと事前に割り当てられた配列は、さまざまなブラウザでテストされています
Nitro(Safari)は、事前に割り当てられた配列により有益です。他のエンジン(V8、Spidermonkey)では、事前配分は効率的ではありません。
事前に配置されたアレイテスト
// empty arrayvar arr = []; for(var i = 0; i <1000000; i ++){arr [i] = i;} // pre-allocated arrayvar array(1000000); for(var i = 0; i <10000; i ++){arr [i] = i;}Webアプリケーションの世界では、速度がすべてです。列の総数を計算したり、情報を要約したりするのに数秒かかるテーブルアプリケーションを使用したいユーザーはいません。これが、コード内のパフォーマンスのあらゆるビットを絞りたい重要な理由です。
画像ソース:Olof Forsbergごと。
アプリケーションのパフォーマンスを理解して改善することは非常に便利ですが、それも困難です。パフォーマンスの問題点を解決するために、次の手順をお勧めします。
以下に推奨されるツールとテクニックの一部は、あなたを助けることができます。
JavaScriptコードスニペットのベンチマークを実行してパフォーマンスをテストする方法は多くあります。一般的な仮定は、ベンチマークが2つのタイムスタンプを単純に比較することです。このパターンはJSPERFチームによって指摘されており、SunSpiderとKrakenのベンチマークスイートで使用されています。
var totaltime、start = new date、iterations = 1000; while(iterations-){//コードスニペットがここに行きます} //合計時間→撮影した数百万秒//コードスニペット1000タイムストータル時間= new date-startを実行するここでは、テストするコードがループに配置され、セット数が実行されます(たとえば、6回)。この後、開始日は終了日から差し引かれ、ループで操作を実行するのにかかった時間が導出されます。
ただし、このベンチマークは、特に複数のブラウザーと環境でベンチマークを実行したい場合は、あまりにも単純なことを行います。ゴミコレクター自体は、結果に特定の影響を与えます。 Window.performanceなどのソリューションを使用しても、これらの欠点を考慮する必要があります。
コードのベンチマーク部分のみを実行するか、テストスイートを作成するか、ベンチマークライブラリをコードするかに関係なく、JavaScriptベンチマークは実際には思っている以上のものです。より詳細なガイドベンチマークについては、Mathias BynensとJohn-David Daltonが提供するJavaScriptベンチマークを読むことを強くお勧めします。
Chrome開発者ツールは、JavaScript分析を適切にサポートしています。この機能を使用して、どの関数がほとんどの時間を占有するかを検出して、最適化できます。これは重要であり、コードのわずかな変更でさえ、全体的なパフォーマンスに大きな影響を与える可能性があります。
Chrome開発者ツール分析パネル
分析プロセスは、コードパフォーマンスのベースラインを取得し始め、タイムラインの形でそれを現れます。これにより、コードが実行にかかる時間がわかります。 [プロファイル]タブは、アプリケーションで何が起こっているかについてのより良い視点を提供します。 JavaScript CPU分析ファイルは、コードで使用されるCPU時間の量を示し、CSSセレクター分析ファイルはセレクターの処理に費やされる時間を示し、ヒープスナップショットはオブジェクトで使用されているメモリの量を示します。
これらのツールを使用すると、機能的または運用性のパフォーマンスの最適化が実際に効果的であるかどうかを測定するために、分離、調整、再分析を測定できます。
[プロファイル]タブには、コードのパフォーマンス情報が表示されます。
分析の良い紹介、Zack GrossbartのJavaScriptプロファイリングをChrome開発者ツールでお読みください。
ヒント:理想的には、分析がインストールされているアプリケーションや拡張機能の影響を受けないようにしたい場合は、 --user-data-dir <empty_directory>フラグを使用してChromeを起動できます。ほとんどの場合、このメソッド最適化テストで十分でなければなりませんが、時間がかかります。これがV8ロゴが役立つものです。
Google内では、Chrome開発者ツールは、Gmailなどのチームで広く使用されており、メモリリークの検出とトラブルシューティングを支援しています。
Chrome開発者ツールのメモリ統計
メモリは、プライベートメモリの使用量、JavaScriptヒープのサイズ、DOMノードの数、ストレージクリーニング、イベントリスニングカウンター、ガベージコレクターをカウントします。 Loreena Leeの「3スナップショット」テクノロジーを読むことをお勧めします。この手法の重要なポイントは、アプリケーションで何らかの動作を記録し、ガベージコレクションを強制し、DOMノードの数が予想されるベースラインに復元されているかどうかを確認し、3つのヒープのスナップショットを分析して、メモリリークがあるかどうかを判断することです。
シングルページアプリケーションのメモリ管理(Angularjs、Backbone、Emberなど)は非常に重要であり、ページを更新することはほとんどありません。これは、メモリリークが非常に明白であることを意味します。デバイスのメモリが限られており、電子メールクライアントやソーシャルネットワークなどのアプリケーションが長い間実行されているため、モバイル端子のシングルページアプリケーションには落とし穴がたくさんあります。能力が大きければ大きいほど、責任が重い。
この問題を解決するには多くの方法があります。バックボーンでは、dispose()を使用して古いビューと参照(現在バックボーン(エッジ)で利用可能です。この関数が最近追加され、ビューの「イベント」オブジェクトにハンドラーが追加され、イベントリスナーを削除し、3番目のパラメーターのモデルまたはコレクション(コールバックコンテキスト)を介してイベントリスナーを削除します。リスナーは、要素が削除されていることを検出すると、メモリリークを避けます。
デリック・ベイリーからの賢明なアドバイス:
イベントと参照がどのように機能するかを理解する代わりに、標準ルールに従ってJavaScriptのメモリを管理します。データをユーザーオブジェクトでいっぱいのバックボーンコレクションにロードする場合は、コレクションをクリアしてメモリを引き受けなくするようにする必要があります。コレクションへのすべての参照とコレクションのオブジェクトへの参照が必要です。使用される参照が明確になると、リソースがリサイクルされます。これは、標準のJavaScriptガベージコレクションルールです。
この記事では、DerickはBackbone.jsを使用する際に多くの一般的なメモリの欠陥をカバーし、これらの問題を解決する方法をカバーしています。
FelixGeisendörferによるノードのデバッグメモリリークに関するチュートリアルは、特により広いスパスタックの一部を形成する場合にも読む価値があります。
ブラウザがドキュメント内の要素を再レンダリングする場合、それらを再計算し、それらの位置とジオメトリをリフローと呼ぶ必要があります。リフローはブラウザでユーザーの操作をブロックするため、リフロー時間の改善が改善されることを理解することは非常に役立ちます。
リフロータイムチャート
バッチでリフローまたは再描画をトリガーする必要がありますが、これらの方法は適度に使用します。また、DOMに対処しないようにすることも重要です。軽量のドキュメントオブジェクトであるDocumentFragmentを使用できます。ドキュメントツリーの一部を抽出したり、新しいドキュメント「フラグメント」を作成する方法として使用できます。 DOMノードを絶えず追加する代わりに、ドキュメントフラグメントを使用して過度のリフローを避けた後、DOM挿入操作を1回だけ実行することをお勧めします。
たとえば、要素に20個のdivを追加する関数を書き込みます。毎回要素にdivを追加するだけで、これは20の反射をトリガーします。
関数adddivs(element){var div; for(var i = 0; i <20; i ++){div = document.createelement( 'div'); div.innerhtml = 'heya!'; element.AppendChild(div); }}この問題を解決するために、代わりにDocumentFragmentを使用できます。一度に新しいDIVを追加できます。完了後にDOMにドキュメントフラグを追加すると、1回だけリフローがトリガーされます。
関数adddivs(element){var div; //新しい空のドキュメントフラグを作成します。 var fragment = document.createdocumentfragment(); for(var i = 0; i <20; i ++){div = document.createelement( 'a'); div.innerhtml = 'heya!'; fragment.appendChild(div); } element.AppendChild(fragment);}Webをより速くする、JavaScriptメモリの最適化、およびメモリリークの検索を参照してください。
JavaScriptメモリの漏れを発見するために、Google開発者(MarjaHölttäandJochen Eisinger)は、Chrome開発者ツールと組み合わせて動作してヒープのスナップショットを取得し、どのオブジェクトがメモリリークを引き起こしているかを検出するツールを開発しました。
JavaScriptメモリリーク検出ツール
このツールの使用方法に関する完全な記事があります。自分でメモリリーク検出器プロジェクトページにアクセスすることをお勧めします。
このようなツールが開発ツールに統合されていない理由を知りたい場合は、2つの理由があります。もともとは、外部ツールとしてより適した閉鎖ライブラリでいくつかの特定のメモリシナリオをキャプチャするのに役立つように設計されていました。
Chromeは、より詳細なエンジン最適化出力の結果を得るために、いくつかのフラグをV8に直接渡すことをサポートします。たとえば、これはV8の最適化を追跡できます。
"/applications/google chrome/google chrome" -js-flags = "-trace-opt-trace-deopt"
WindowsユーザーはChrome.exe js-flags = "trace-opt trace-deopt"を実行できます
アプリケーションを開発するときは、以下のV8ロゴを使用できます。
V8の処理スクリプトは *(アスタリスク)を使用して最適化された関数を識別し、〜(波状)を使用して最適化されていない関数を表します。
V8のロゴとV8のインテリアの仕組みについて詳しく知りたい場合は、V8内部に関するVyacheslav Egorovの優れた投稿を読むことを強くお勧めします。
High Precision Time(HRT)は、システム時間とユーザーの調整に影響を与えないサブミリ秒レベルの高精度時間インターフェイスです。新しい日付とdate.now()よりも正確な測定方法と見なすことができます。これは、ベンチマークを書くのに大いに役立ちます。
Highcision Time(HRT)は、現在のサブミリ秒の時間精度を提供します
現在、HRTはwindom.performance.webkitnow()のChrome(Stableバージョン)で使用されていますが、接頭辞はChrome Canaryで破棄されているため、window.performance.now()を呼び出すことができます。 Paul IrishはHTML5RocksにHRTについてもっと投稿しました。
現在の正確な時間がわかったので、ページのパフォーマンスを正確に測定できるAPIはありますか? Well, now there is a Navigation Timing API that provides an easy way to get accurate and detailed time measurement records when web pages are loaded and presented to users. You can use window.performance.timing in console to get time information:
显示在控制台中的时间信息
我们可以从上面的数据获取很多有用的信息,例如网络延时为responseEnd fetchStart,页面加载时间为loadEventEnd responseEnd,处理导航和页面加载的时间为loadEventEnd navigationStart。
正如你所看到的,perfomance.memory的属性也能显示JavaScript的内存数据使用情况,如总的堆大小。
更多Navigation Timing API的细节,阅读Sam Dutton的Measuring Page Load Speed With Navigation Timing。
Chrome中的about:tracing提供了浏览器的性能视图,记录了Chrome的所有线程、tab页和进程。
About:Tracing提供了浏览器的性能视图
这个工具的真正用处是允许你捕获Chrome的运行数据,这样你就可以适当地调整JavaScript执行,或优化资源加载。
Lilli Thompson有一篇写给游戏开发者的使用about:tracing分析WebGL游戏的文章,同时也适合JavaScript的开发者。
在Chrome的导航栏里可以输入about:memory,同样十分实用,可以获得每个tab页的内存使用情况,对定位内存泄漏很有帮助。
我们看到, JavaScript的世界中有很多隐藏的陷阱,且并没有提升性能的银弹。只有把一些优化方案综合使用到(现实世界)测试环境,才能获得最大的性能收益。即便如此,了解引擎是如何解释和优化代码,可以帮助你调整应用程序。
测量,理解,修复。不断重复这个过程。
图片来源: Sally Hunter
谨记关注优化,但为了便利可以舍弃一些很小的优化。例如,有些开发者选择.forEach和Object.keys代替for和for..in循环,尽管这会更慢但使用更方便。要保证清醒的头脑,知道什么优化是需要的,什么优化是不需要的。
同时注意,虽然JavaScript引擎越来越快,但下一个真正的瓶颈是DOM。回流和重绘的减少也是重要的,所以必要时再去动DOM。还有就是要关注网络,HTTP请求是珍贵的,特别是移动终端上,因此要使用HTTP的缓存去减少资源的加载。
记住这几点可以保证你获取了本文的大部分信息,希望对你有所帮助!
原文:http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
作者:Addy Osmani