最近こういうことよくやらなきゃなのでまとめました。
以下のリンクにある動画とスライドのまとめみたいなものです!
自分用なので、細かいことは書いてませんが。
参考:Memory Management Masterclass with Addy Osmani - YouTube
参考:JavaScript Memory Management Masterclass // Speaker Deck
動画のがわかりやすいので、時間のある人はぜひぜひ。
0. はじめに
- パフォーマンスチェックには、DevToolsのTimelineとProfileが便利
- Chromeのタスクマネージャでもメモリの使用量とかはわかるけど、DevTools使ったほうがわかりやすいし使うべき
1. DevToolsを使ったDEMO
Sawtooth Curve
- DevToolsのTimelineで、所謂ノコギリ型にメモリの使用量が推移してたら、オブジェクトが頻繁に生成・破棄されてる
- つまりGCが頻繁に走ってるので、パフォーマンスがよろしくない
- 他にもGCのタイミングでDOMノード・メモリが減らないとかあれば、リークしてる可能性が高い
V8's Hidden Classes
- V8は処理速度向上のために、内部的に最適化したオブジェクトの情報を保持してる(Fast object)
- 不要になったからといって、deleteでオブジェクトのプロパティを削除したりすると、最適化が解かれてしまう(Slow object)
- Slow objectは、Fast objectの15倍メモリを食う
そのほか気にすべきもの
- クロージャ内で外側のスコープへの参照持ちっぱにしないように
- DOMで子ノードの参照が生き続けて、親ノードがGCされないあるあるに注意
- clearTimeout忘れで参照が残り続けてて、親オブジェクトがGCされないあるあるにも注意
ES6 WeakMaps
- キーに使ってるオブジェクトがGCされた時、WeakMap側のキーも消えてくれる
用途がイマイチぱっとしない人は、以下の参考リンクがわかりやすかったです。
2. チートシート
プロファイリングの前に
Design fitst.
Code from the design.
Then profile the result.
最適化に着手するのは、然るべきタイミングで、ってことですね。
肝に銘じておきます・・・。
メモリまわりのチェック項目
まず、使いすぎてないかメモリの動きを見る
- タスクマネージャでもいいし
- TimelineのMemoryビューでもいい
- MemoryビューならDOMノードの数とかイベントリスナーの数とかも見れるよ
その後でリークを疑う
- DevToolsのProfileのObject allocation trackerが便利
- Heap profilerでSnapshot取って比較するのもよい
GCの頻度はどうか
- GCが頻繁に走ってたら要注意
普段から気にすべき項目
- DOMへの参照まわりは特に注意
- 循環参照は避けよう
- スコープは適切に
- イベントはちゃんと剥がそう
- キャッシュは消すことも踏まえて、貯めっぱなしにしないように
3. メモリマネジメントの基礎
先にコレを読んでおくとしっくりくるはず。
- WindowオブジェクトやGlobalオブジェクト等、すべてはRootのノードからはじまる
- Rootノード -> Objectノード -> Scalarノード
- Rootノードから辿れなくなったノードが、GCの対象になる
- ゆえに、Rootから深いところでノードが生き続けると、その根本まで全てGCされない
- Retained path(s)とは、自身とそのノードから辿れるノードの範囲のこと
- Retained size(s)とは、自身とそのノードから辿れるノードの総サイズのこと
メモリリークとは
使えるメモリが徐々に少なくなって(やがて足りなくなる)いくこと。
- メモリプールがいっぱいになると、GCが走る
- だいたいはmsレベルではあるものの、時間がかかる処理
- この間、本体アプリの処理も止まる
- GCされることを踏まえてメモリは使うこと
4. V8のメモリ管理
ここも先にさっきのリンク読んだ方がわかりやすいかも・・。
- V8は世代別GC
- Young -> Oldというようにわけられる
- GCの度に生き残ってたものが、よりOldになっていく
- TimelineでGCイベントとして見れるのは、Youngの方
- OldなGCは、他の処理と平行して行われてる
- \Triggering a collection pauses your app!/
performance.memory
- jsHeapSizeLimit: 使用可能なJavaScriptヒープのメモリサイズ(bytes)
- totalJSHeapSize: その内、既に割り当てられたメモリサイズ(bytes)
- usedJSHeapSize: その内、現在使用中のメモリサイズ(bytes)
5. Chrome DevToolsおさらい
Timeline
- やはりTimeline便利
- ゴミ箱のアイコンで、強制的にGCできるので、計測する前にやるといいかも
Profile
- SnapshotをComparisonビューで見るのおすすめ
- 背景が黄のノードは参照されてる
- 背景が赤のノードは単一のノードから参照されてるやつなので要注意
- DistanceはRootノードからの距離なので、極端に深いやつは要注意
- Shallow Sizeが小さくとも、ノードである限りGCを阻害することができるので注意
- クロージャにも名前つけとくと、後から調べやすくて良い
Snapshotを撮るテク
- 怪しい箇所を予想する
- まず撮る
- 怪しいと思われる動きをしてみる
- もっかい撮る
- 見比べる
Object allocation tracker
(何回も出てきてるけど)DevTools -> Profile -> Record Heap Allocationsで使う。
- Timelineみたくリアルタイムに見れる
- 青のバーの高さはメモリの使用量の目安に
Allocation Stack Traces
Devtools Setting -> General -> Profiler -> Record heap allocation traces でONに。
どこでメモリが使われたかまで追えるように!
CPU ProfileのChartビュー
- CPUがどのJavaScriptコードを実行したかが時系列でわかる
- 積み上がるのはFunctionCall
- それより実行に時間くってる横幅デカいやつを要チェックすべし
6. おわりに
なんて濃ゆいセッションなんや・・・。
(●・̆⍛・̆●) < Webフロントエンド大変過ぎて生きるのがつらい
@addyosmani++
ありがたやー。