🧊

JavaScript Memory Management Masterclass with Addy Osmaniの要点まとめ

最近こういうことよくやらなきゃなのでまとめました。

以下のリンクにある動画とスライドのまとめみたいなものです!
自分用なので、細かいことは書いてませんが。

参考: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側のキーも消えてくれる

用途がイマイチぱっとしない人は、以下の参考リンクがわかりやすかったです。

Let's 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. メモリマネジメントの基礎

先にコレを読んでおくとしっくりくるはず。

Gmail スケールの効率的メモリ管理 - HTML5 Rocks

  • 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++
ありがたやー。