こんにちは。平奥です。
これは TECHSCORE Advent Calendar 2017 の 24 日目の記事です。
フロントエンドエンジニアが常日頃システムに対して注意を払わないといけないところ、今回はパフォーマンスとメモリリークについて記載していきたいと思います。
パフォーマンス
開発初期の頃はあまり気にしないことが多いパフォーマンスですが、機能追加を繰り返していくことにより、表示するデータ量が大量になっていたりして、気づくと表示するのに数十秒かかる画面があるというようなこともあったりします。
特にフロントエンド側はクライアント側のマシンスペックに依存しますので、システムがどういうユーザがどのようなスペックのマシンを使用しているかなども調査して表示などのパフォーマンスを維持しないといけません。
また使用するブラウザによって JavaScript エンジンが異なり、パフォーマンスに雲泥の差が出ることがあります。
上記の理由などによりいろんな環境で定期的に計測を行い、パフォーマンスが出ないところは改善を行わないといけません。
パフォーマンス測定
パフォーマンスの計測にはいろいろツールはありますが、Chrome DevTools の Performance パネルが手軽でレンダリングエンジンの行うほとんどのプロファイルを取得することができますのでパフォーマンス計測にはおすすめです。
使い方も簡単で、 Chrome DevTools を起動し、 Performance パネルを選択します。
以下の画面がでてきますので、 「●」 で表示されている Record ボタンを押下します。そうするとプロファイルの計測が開始されます。
開始されたら計測したい処理を実行します。
計測したい処理が終わったら Stop ボタンを押下します。そうすると以下のような画面が表示されます。
計測した結果は詳細のペインに表示されます。
- Summary :処理の実行時間や関数名などの基本的な情報が表示されます
- Buttom Up :パフォーマンスに影響を与えている関数が表示されます
- Call Tree :関数の呼び出し元から順に処理を追っていくことができます
- Event Log :開始時から順に処理が表示されます
このようにパフォーマンスを計測することができ、いろいろな角度から情報を表示することができますので、これらを活用し、重い処理を見つけ出し、パフォーマンスの改善を検討することができます。
メモリリーク
フロントエンドはほとんどが JavaScript で実装されており、ランタイム上でメモリの確保、解放を行う GC (ガベージコレクション) が採用されています。
あまり気にすることのなさそうなメモリリークですが、プログラムの書き方により確保したメモリが GC で回収されずメモリが肥大化し、処理を圧迫し、ブラウザがフリーズしたり、異常終了したりすることがあります。
特にタイマー処理やイベントリスナーの解放忘れなどには注意が必要です。またクロージャーの使い方や循環参照によってもメモリリークを発生させる可能性があるのでよく確認する必要があります。
下の例は clearInterval を呼び出し、メモリリークしないようにタイマー処理を終了しています。
1 2 3 4 5 6 7 8 9 |
var count = 0; var countup = function(){ console.log(count++); }; var id = setInterval(function(){ countup(); if(count > 5){ clearInterval(id); }}, 1000); |
イベントリスナーも使用しなくなった場合は、 removeEventLister を呼び出しメモリを解放するようにしましょう。
JavaScript でメモリリークになるパターンはネットで調べると出てきますので、なるべく日頃からメモリリークを起こさないコーディングを心がけるようにするとよいと思います。
また最近流行の SPA (シングルページアプリケーション) ではリフレッシュを行わないので、さらにメモリリークによる影響がより顕著に表れます。
したがって、SPA を構成しているフレームワークのお作法に従ってメモリを確保しているオブジェクトなどを解放するようにしましょう。
ただし、SPA に関してはフレームワークの不具合やブラウザの問題などでメモリが解放されないなどの事象も発生します。
そういう場合はその事象にもよりますが、実装方法を変更したり、適当な個所で明示的にリロードしたりしてメモリを解放するような処理を追加するようにしましょう。
またメモリリークを起こさないように注意を払っていても、開発者も人間ですからメモリリークを起こすコードは書いてしまいます。
そのため、定期的にメモリリークが起きていないかどうかをチェックすることも必要です。
メモリリーク検知
メモリリークを検知するためには、先ほどパフォーマンスでも使用した、 Chrome DevToos の Performance パネルを使うことで確認することもできます。
その使用方法を以下にご紹介します。
まず Performance パネルで memory にチェックを入れます。
こうすることでメモリの使用量の推移グラフを見ることができます。
先ほどと同じように計測を行います。
以下のようなグラフが出てきた場合、メモリが解放されずどんどん増える傾向にあるので、メモリリークを起こしている可能性があります。青いグラフがメモリ使用量になります。
メモリリークが発生している疑いがある場合は、ヒープのスナップショットをとって確認することも可能です。
ヒープのスナップショットの取り方は以下となります。
Chrome DevToos の Memory パネルで Take Heap SnapShot を選択し、「Take Snapshot」ボタンを押下します。
これでスナップショットを取ることができます。
そして3点観測をすることによって、効率的にメモリリーク箇所を特定することができます。以下の手順で実施します。
- このスナップショットを利用して、まず実行前にスナップショットをとります
- その後メモリリークしていると思われる処理を実行し、スナップショットをとります
- そしてメモリを解放しているであろう処理を実行し、同じくスナップショットをとります
この3点で観測を行い、最初にとったスナップショットのメモリ量と最後にとったスナップショットのメモリ量を比較し、最後のスナップショットの方のメモリ量が多いならメモリリークしている可能性があります。
メモリリークの可能性がある場合、以下の方法で対処します。
最後にとったスナップショットの Summary ビューを確認します。
このビューで表示されているオブジェクトを見ると、黄色や赤色で表示されているオブジェクトが見つかることがあります。
黄色で表示されているオブジェクトは JavaScript からの参照を含むオブジェクトを表します。開発者が誤ってコーディングした JavaScript の処理によってリークしているオブジェクトを探すには、この黄色で表示されているオブジェクトをまず探します。
詳細を見るにはオブジェクトをクリックします。 そうすると Object ペインに詳細な情報が表示されますので、この情報を追ってメモリリークをしている箇所を探すようにします。
まとめ
今回はフロントエンドエンジニアが常日頃注意を払わないといけないところである、パフォーマンスとメモリリークについて取り上げさせていただきました。システムは新機能の追加、バグの改修などでソースは常に変化しているといっても過言ではありません。そういう状態なので、定期的にパフォーマンス計測やメモリリーク行うことは必須であると考えています。
フロントエンドの世界は移り変わりが激しく混沌とした世界です。AltJS、タスクランナー、ビルドツール、トランスパイラ、モジュールバンドラなど、追いつけないスピードで色んなものが生まれ、衰退していきます。
常に最新のトレンドを追いかけて今のシステムに最適なフレームワーク、設計思想、ツールを探し続け選択しなければなりません。
それと並行して現在使っているフレームワークの機能の非推奨化や End of life に怯えながら新しく追加されたフレームワークの機能に適応させる。新しいツールへの移行する最適なタイミングを検討する。
そういうフロントエンドの世界を上手に渡り歩く一助になればと思います。