必要条件

この記事に必要な予備知識

このチュートリアルを始める前に、Adobe Scout ファーストステップガイドをお読みください。

ユーザーレベル

中級

原文 作成日: 2013/6/17

Memory profiling with Adobe Scout

インタラクティブコンテンツを開発する際の第 1 目標は、優れたユーザー体験を届けることです。開発対象がゲームでも、モバイルアプリケーションでも、インタラクティブ Web サイトでも、高速で応答性が高く、利用しやすいものを目指します。単にコードの実行速度を高めるだけでは、この目標は達成できません。コンテンツのメモリ要件についても考慮する必要があります。モバイルデバイスで利用できるメモリ容量は限られています。そのため、開発者が気をつけなければ、ユーザーにかなりの制限をかけることになります。それだけではなく、メモリの割り当てと割り当て解除には時間がかかるので、不要なメモリ割り当てを避けるのが最善策です。

Adobe Scout は Flash コンテンツ内のメモリの問題をデバッグするために不可欠なツールです。Flash Player を使用してブラウザーでコンテンツを実行する場合でも、Adobe AIR でモバイルアプリケーションとしてコンテンツをパッケージ化する場合でも利用できます。この記事では、メモリの問題の特定し、その原因を判断するための Scout の使用法を学習します。特に、Flash Player でのメモリ使用量の管理方法と Scout で表示されるメモリデータの解釈方法を学習します。また、Scout 1.1 で導入されたメモリ割り当てのトラッキングという新機能の使用法についても学習します。この機能を使用すれば、メモリ内に存在する特定のオブジェクトとその割り当て位置をすぐに特定できます。

この記事では、Scout のメモリのプロファイリングに関連する機能のみを扱っています。Scout の使用経験がない場合や、パフォーマンスやグラフィックのプロファイリングに興味がある場合は、まずファーストステップガイドをお読みください。

Flash Player のメモリモデル

Scout を使用してメモリの問題をデバッグする前に、Flash Player のメモリ管理方法について理解しておくと役立ちます。Flash Player には内部的に、2 種類の割り当て可能なメモリプールがあり、それぞれ「マネージドメモリ」と「アンマネージドメモリ」と呼ばれます。

「マネージドメモリ」とは、ガベージコレクターによって制御される領域であり、明示的な割り当て解除は必要ありません。すべての ActionScript オブジェクトと、Flash Player によって内部的に使用される一部のオブジェクトがマネージドメモリに格納されます。基本的に、Flash Player はオペレーティングシステムから大きなメモリチャンクを割り当てて、このチャンクに新しいオブジェクトを格納します。メモリチャンクの領域が不足し始めたときにも、Flash Player はオペレーティングシステムにメモリの追加を要求することは避け、「ガベージコレクター」を実行して、既存のメモリの一部を回収できないかを確認します。ガベージコレクターはマネージドメモリを調査し、デッド状態のオブジェクトを判定して、それらのオブジェクトが使用しているメモリを回収します(このプロセスは「マーク&スウィープ」と呼ばれます)。「デッド」オブジェクトとは、参照の手段がないことにより ActionScript からアクセスできないオブジェクトのことです。ガベージコレクターで十分なメモリを回収できない場合は、オペレーティングシステムにメモリの追加を要求し、マネージドメモリプールを拡張します。

「アンマネージドメモリ」とは、Flash Player により直接制御される領域であり、不要になった各オブジェクトで明示的に割り当て解除する必要があります。アンマネージドメモリは ActionScript から直接割り当てることはできず、水面下で、画像、SWF ファイル、ByteArray などの一部の大きなデータ構造がアンマネージドメモリに格納されます。このデータを参照する ActionScript オブジェクトを作成した場合(表示オブジェクトを画像から作成した場合など)、そのオブジェクトは基盤のアンマネージドメモリに対するハンドルとして機能します。ActionScript オブジェクトによって参照されているアンマネージドメモリは、(参照しているのがそのオブジェクトだけの場合に限り)そのオブジェクトがガベージコレクションされたときに割り当て解除されます。

図1 に、Flash Player とこれらのメモリプールとの相互作用について示します。グレーのオブジェクトは、参照の手段がないことから技術的にデッド状態となっていますが、(参照されるビットマップと共に)割り当て解除されるのは、次回にガベージコレクターが実行されるときです。ガベージコレクターの仕組みについて詳しくは、Flash Player および Adobe AIR のガベージコレクションの内部的な仕組みを参照してください。

Scout を使用したメモリ使用量のトラッキング

Scout で新しいセッションを開始する前に、「Settings for New Sessions(新規セッションの設定)」で、収集するデータを選択できます。選択したデータにかかわらず、すべてのセッションで、Flash Player 内部の状況に関するいくつかの基本的なデータが記録されます(コンテンツのメモリ使用量に関する概要レベルのデータなど)。このデータは、フレームタイムラインパネルおよびSummary(概要)パネルの「Memory(メモリ)」セクションで確認できます(図 2 を参照)。

フレームタイムラインのグラフには、各フレームの末尾の時点での Flash Player によるメモリ使用量が、カテゴリに分類されて表示されます。これは、メモリ使用量の定期的なスナップショットに過ぎませんが、「メモリリーク」の発生を特定するのに役立ちます。メモリリークが発生すると、メモリ使用量が時間とともにじわじわと増加し、ガベージコレクターを実行しても減少しません。

Summary(概要)パネルには、選択したフレーム範囲におけるメモリ使用量が表示されます。デフォルトでは、現在のメモリ使用量が表示されます。これは、選択範囲の最後のフレームの末尾の時点におけるメモリ使用量を分析したものです。歯車ボタンをクリックすると、選択したフレーム範囲におけるメモリ使用量の「average(平均)」と「peak(ピーク)」の間で表示を切り替えることができます(図 3 を参照)。これらの設定の正確な意味は次のとおりです。

  • 「average memory(メモリ平均)」は、時間に対する各カテゴリ内の加重平均です。これは、フレームあたりのメモリ平均とは異なります。例えば、80 ms の長さで合計 10 MB を使用するフレームと、20 ms の長さで合計 90 MB を使用するフレームの 2 つを選択した場合、メモリ合計の平均は ((10 MB x 80 ms) + (90 MB x 20 ms)) / (80 ms + 20 ms) = 26 MB となります。
  • 「peak memory(ピーク時のメモリ)」は、選択範囲における各カテゴリの最大値です。そのため、それぞれのカテゴリのピーク値を合計すると、ピーク時のメモリ使用量よりも多くなることがあります。これについて説明するために、次の例を作りました。2 つのフレームがあり、両方とも使用済みメモリが 100 MB で、一方のフレームはビットマップが 90 MB、ActionScript オブジェクトが 10 MB であり、もう一方のフレームはビットマップが 10 MB、ActionScript オブジェクトが 90 MB であるとします。この場合、ビットマップのピーク時のメモリ使用量は 90 MB で、ActionScript オブジェクトのピーク時のメモリ使用量も 90 MB となります。これらの合計は、ピーク時のメモリ使用量 100 MB よりも多くなります。

Summary(概要)パネルのデータを詳しく見ると、Scout で「total(合計)」メモリと「used(使用済み)」メモリが区別されていることが分かります。割り当ての速度を上げるために、Flash Player ではオブジェクトの割り当てが必要となるたびにオペレーティングシステムにメモリの追加を要求するわけではありません。そうではなく、オペレーティングシステムから大きなメモリブロックを取得し、これらのブロック内で内部的な割り当てを実行します。これは、マネージドメモリとアンマネージドメモリの両方に当てはまります。「total memory(メモリ合計)」は、オペレーティングシステムから取得したすべてのブロックの合計です。一方、「used memory(使用済みメモリ)」は、これらのブロックのうち、Flash Player による実際のメモリ使用量を表します。

Scout にレポートされるメモリ合計は、通常 Mac OS X アクティビティモニターや Windows タスクマネージャーに表示される値よりも少し小さくなります。この理由は、ファイルハンドルやグラフィックリソースなどの一部のリソースが、Flash Player ではなくオペレーティングシステムによって直接割り当てられていることです。この追加のメモリ使用量は少なく、通常は気にする必要はありません。

メモリの問題を特定できるように、Summary(概要)パネルでは使用済みメモリが次の 5 つのトップレベルカテゴリに分類されています。

  • ActionScript Objects(ActionScript オブジェクト) - Flash Player が使用しているマネージドメモリ。大半は ActionScript から実行される割り当てによるものですが、Flash Player がマネージドメモリを使用して内部的に割り当てる一部のオブジェクトも含まれます。これらの内部オブジェクトは通常、このカテゴリのメモリの約 10 MB を占めますが、この量はコンテンツによって異なります。
  • Bitmap(ビットマップ) – ロードした画像などのグラフィックリソースに使用されるメモリ、および Flash Player でレンダリング(キャッシュ、フィルターの適用など)の実行中に内部的に使用されるメモリ。これらはアンマネージドメモリから割り当てられますが、多くの場合は表示リスト上の項目などの ActionScript オブジェクトに関連付けられます。
  • ByteArray - ByteArray オブジェクトによって使用される基盤メモリ。ByteArray オブジェクトはアンマネージドメモリに格納されます。FlasCC を使用している場合は、「DomainMemory」がサブカテゴリとして表示されます。これは、FlasCC で C/C++ オブジェクトを格納するために内部的に使用される ByteArray です。
  • SWF Files(SWF ファイル) - ロードした SWF(メイン SWF を含む)を格納するために使用されるメモリ。これはアンマネージドメモリです。
  • Other(その他) - 上記のカテゴリに分類されない使用済みメモリ。

ビットマップカテゴリは多くのサブカテゴリに分類されます。Scout では、該当なしのカテゴリは非表示となるので、コンテンツの実行時にすべてのサブカテゴリが表示されない場合があります。ビットマップのサブカテゴリは次のとおりです。

  • Images(画像) - Loader インスタンスを使用してダウンロードされた画像(通常は圧縮済み)を格納するために使用されるメモリ。JPG、GIF、PNG および ATF ファイルが含まれます。ただし、[Embed] メタデータを使用して SWF ファイル内に画像を埋め込んでいる場合、このメモリは「Images(画像)」ではなく「SWF Files(SWF ファイル)」に表示されます。
  • Bitmap DisplayObjects(ビットマップ DisplayObject) – Flash Player でロードして解凍した画像を格納するために使用されるメモリ。Loader インスタンスを使用してロードした場合も、SWF ファイル内の埋め込みリソースである場合も当てはまります。また、ミップマップにより使用されるメモリも含まれます。
  • BitmapData - BitmapData オブジェクト(BitmapData のサブクラスのオブジェクトを含む)を格納するために使用されるメモリ。このデータはアンマネージドメモリに格納されます。マネージドの ActionScript BitmapData オブジェクトは、このメモリへのハンドルに過ぎません。
  • Bitmap Filter Buffers(ビットマップフィルターバッファー) - 表示リストオブジェクトにフィルターを適用した結果や、拡大/縮小などの効果を適用した結果を格納するために使用されるメモリ。
  • CacheAsBitmap Buffers(CacheAsBitmap バッファー)cacheAsBitmap または cacheAsBitmapMatrix プロパティを設定している場合に、表示リストオブジェクトのレンダリングの結果をキャッシュするために使用されるメモリ。
  • Main Screen Buffer(メイン画面バッファー) - 表示リストをレンダリングする際に Flash Player によって使用されるメモリ。このバッファーは、レンダリングパスの最後に画面にコピーされます。また、このバッファーは、合成ビデオなどの他のソースのターゲットにもなります。
  • Other Bitmap Memory(その他のビットマップメモリ) - テキストのレンダリングなど、他のレンダリング処理に使用されるメモリ。このような名前にもかかわらず、この項目には URLRequest を使用してダウンロードしたサウンドも含まれます。Flash Player では、サウンドリソースとビットマップリソースに対して同じメモリマネージャーが使用されるからです。

「Other(その他)」カテゴリには、他のカテゴリのいずれかにうまく分類されないような、様々な目的で使用されるメモリが含まれます。このカテゴリにも、次のような多くのサブカテゴリがあります。

  • Network Buffers(ネットワークバッファー) - ネットワーク経由で到着した未処理のパケットを格納するため、および送信待ちのパケットを格納するために Flash Player によって使用されるメモリ。
  • Telemetry Overhead(Telemetry のオーバーヘッド) - Flash Player のプロファイラーによって使用されるメモリ。このオーバーヘッドは、Scout の使用中にのみ発生するので、気にする必要はありません。
  • Other Players(別のプレイヤー) - Web ブラウザーで Flash Player プラグインを実行している場合に、プロファイリングを実行しているインスタンス以外のプレイヤーインスタンスで使用されるメモリ。同じページ上の他のコンテンツや、別のブラウザータブで実行している他のコンテンツなどが当てはまります。他の ActionScript Worker によって使用されるメモリもこのカテゴリに含まれます。
  • Uncategorized(未分類) - Flash Player で使用中だけれども現時点でトラッキング対象ではないその他のメモリ。サウンドバッファーとビデオバッファー、XML データ、Stage3D で使用されるメモリ、Flash Player で内部的に使用される多数の小さなデータ構造などが当てはまります。今後の Scout バージョンでカテゴリを追加する予定です。

マネージドメモリを使用するカテゴリは ActionScript オブジェクトだけであることに注意してください。コンテンツで大量のオブジェクトの割り当てと破棄を繰り返している場合、このカテゴリはのこぎり歯状パターンに従う傾向にあります(図 4 を参照)。Flash Player でフレームごとに新しい ActionScript オブジェクトを割り当てると、メモリ使用量は徐々に増加します。しばらくすると、それ以上のオブジェクトを割り当てるための領域がなくなり、ガベージコレクションがトリガーされます。この時点で、参照の残っていないオブジェクトのメモリが Flash Player によって回収され、メモリ使用量が急減します。ただし、メモリ合計は減少しません。Flash Player では、オペレーティングシステムから既に取得したメモリは維持され、おそらくは再度使用されることになります。

この動作の問題は、ガベージコレクションは計算量の多い処理であり、その実行時にコンテンツの実行が断続的になる可能性があることです。特定の型のオブジェクトの回転率が高い場合、オブジェクトプーリングを使用して、新しいオブジェクトを割り当てなくても同じオブジェクトを再利用できるようにすると効果的です。言うまでもなく、まずは問題の原因となっているオブジェクトを明らかにする必要があります。幸いにも、Scout 1.1 にはこれを実現するためのメモリ割り当てのトラッキングという新機能が搭載されています。

Scout を使用したメモリ割り当てのトラッキング

メモリ内にあるオブジェクトを詳しく分析するには、「Settings for New Sessions(新規セッションの設定)」の「Memory Allocation Tracking(メモリ割り当てのトラッキング)」をオンにします(図 5 を参照)。この結果、Flash Player でメモリの割り当てと割り当て解除が記録され、そのデータが Scout に送信されるようになります。Flash Player は、ActionScript コードが実行するすべての割り当てをトラッキングします。また、開発者の代わりに実行される大きな割り当て(具体的には、ビットマップ、ByteArray および SWF ファイルのカテゴリに分類される項目)もトラッキングします。Flash Player が実行するすべての割り当てが記録されるわけではなく、コンテンツの直接の実行結果である割り当てのみが記録されます。

Web ブラウザーでコンテンツを実行する場合、「Memory Allocation Tracking(メモリ割り当てのトラッキング)」設定には、Flash Player のデバッガーバージョンが必要になります。AIR コンテンツに対しては特別な操作を行う必要はありません。この設定を使用すれば、リリースコンテンツのプロファイリングを実行できます。また、この機能を使用するには、コンテンツで「Advanced Telemetry(詳細 Telemetry)」を有効にする必要もあります。有効にする方法については、ファーストステップガイドをお読みください。

Flash Player で収集されたデータは、Scout のMemory Allocations(メモリ割り当て)パネルで確認できます。このパネルのデフォルト表示では、選択したフレーム範囲内における、オブジェクトが割り当てられた ActionScript スタックトレースが表示されます(図 6 を参照)。このスタックトレースを展開すると、各クラスの割り当て済みのオブジェクト数を確認できます。また、Flash Player の内部的な処理によって ActionScript の外部で発生した割り当てについても確認できます。例えば、図 6 では「Preparing ActionScript Bytecode(ActionScript バイトコードの準備)」で 20 の割り当てが発生しています。

デフォルトでは、Memory Allocations(メモリ割り当て)パネルには、選択範囲の最後のフレームの末尾の時点でまだ生存状態であったオブジェクトの割り当てのみが表示されます。つまり、選択したフレーム範囲内で、割り当てられた後にガベージコレクションされたオブジェクトは表示されません。既にガベージコレクションされたオブジェクトを含むすべてのオブジェクトを確認する場合は、「Hide Garbage-Collected Objects(ガベージコレクションされたオブジェクトを非表示)」フィルターをオフにできます(図 7 を参照)。

重要なこととして、Memory Allocations(メモリ割り当て)パネルには、選択したフレーム範囲内で割り当てられたオブジェクトのみが表示されます。それより前のフレームで割り当てられたオブジェクトは、選択中のフレーム範囲内で生存している場合でも、このパネルには含まれません。フレーム n で生存状態のすべてのオブジェクトを表示する場合は、Scout でフレーム 1 から n までを選択する必要があります。

トップダウンビューは、コードの実行との関係で割り当てがいつ発生したかを把握するのに役立ちますが、オブジェクトの型ごとの割り当て数合計を知るだけで十分な場合もあります。Bottom-Up Objects(ボトムアップオブジェクト)ビューには、各クラスの割り当て数が表示されます(図 8 を参照)。オブジェクトを展開すると、その割り当ての原因を参照できます。

オブジェクトの色が関数呼び出しの色と異なるのはなぜなのか、疑問に思われるかもしれません。その理由は、Summary(概要)パネルに示される、オブジェクトが属しているメモリカテゴリに従ってオブジェクトが色分けされている点にあります。ほとんどの割り当ては ActionScript オブジェクトですが、Scout にはその他の大きな割り当ても示されます。例えば、図 9 でメモリの大半を占めている割り当ては、ビットマップ DisplayObject によるものです。これらは、テクスチャのアップロード時および画像の解凍時に Flash Player によって内部的に作成されたものだと分かります。

割り当て頻度が最も高いクラスの確認に加えて、頻度の最も高い割り当てを実行した関数を確認するのも効果的です。この確認は、特定の関数を最適化するのに役立ちます。例えば、新しいオブジェクトを毎回割り当てる代わりに、オブジェクトを再利用することが可能です。Bottom-Up Functions(ボトムアップ関数)ビューには、別の関数を呼び出して間接的に実行されたのではなく、直接実行された割り当て数に従って、各関数の情報が表示されます(図10 を参照)。

関数を展開して、その関数のすべての呼び出しで割り当てられたオブジェクトの分析と、その関数を呼び出した他の関数について確認できます。

これまでは、Memory Allocations(メモリ割り当て)パネルを使用した割り当ての調査方法を学習してきましたが、このパネルでは、オブジェクトの割り当て解除のタイミングについても確認できます。Allocations(割り当て)設定を「Deallocations(割り当て解除)」に変更すると、選択したフレーム範囲の間に割り当て解除されたすべてのオブジェクトを確認できます。また、Activity Sequence(アクティビティシーケンス)パネルかTop Activities(上位アクティビティ)パネルで、Memory Allocations(メモリ割り当て)パネルにフィルターを適用することもできます(図11を参照)。このフィルターは、ActionScript パネルへのフィルターと同じように動作します。このフィルターを使用して、特定のガベージコレクションで割り当て解除されたオブジェクトを確認できます。

ライブセッションのプロファイリング時に、Flash Player の現在の実行を強制的に停止し、フルガベージコレクションを実行できます。そのためには、Memory Allocations(メモリ割り当て)パネルの左上隅にあるボタンをクリックします(図 12 を参照)。これは、Scout で調査しているオブジェクトが本当に生存状態であり、ガベージコレクション待ちの状態ではないことを確認するために便利です。

メモリのプロファイリングにおける既知の問題

Scout を使用してメモリのプロファイリングを行う場合に、いくつかの注意事項があります。

  • iOS 用に AOT でコンパイルされたコンテンツ内で静的に初期化された配列は、Memory Allocations(メモリ割り当て)パネルには表示されません。この問題は AIR の次のバージョンで修正されますが、それまでの間は、配列をコンストラクター内で初期化することでこの問題を回避できます。
  • BitmapData のサブクラスであるオブジェクトには、Memory Allocations(メモリ割り当て)パネルで BitmapData カテゴリの色が割り当てられません。Scout では、アプリケーションのクラス階層は把握されないので、これらのオブジェクトは ActionScript オブジェクトとして色分けされます。Summary(概要)パネルでは、これらのオブジェクトのメモリ使用量は BitmapData カテゴリに含まれます。
  • 初期 SWF ファイルおよびメイン画面バッファーがMemory Allocations(メモリ割り当て)パネルに表示されないことがあります。ただし、これらはSummary(概要)パネルのデータには必ず含まれます。
  • Memory Allocations(メモリ割り当て)パネルで ActionScript オブジェクトによるメモリ使用量を合計した場合、Summary(概要)パネルでレポートされる値よりも小さくなります。これは、ActionScript オブジェクトカテゴリに、Flash Player によって内部的に割り当てられる一部のメモリが含まれるからです。このメモリもマネージドメモリを使用します。この点については気にする必要はありません。
  • Stage3D によって内部的に使用されるメモリは、Summary(概要)パネルでは明示的に識別されません。テクスチャやロードしたその他のリソースは(「Images(画像)」や「ByteArray」などで)確認できますが、水面下で OpenGL や DirectX によって使用されるメモリは「Uncategorized(未分類)」カテゴリに分類されます。
  • ビデオおよびサウンドの再生のためにバッファーによって使用されるメモリは、Summary(概要)パネルでは明示的に識別されません。このメモリは「Uncategorized(未分類)」カテゴリに分類されます。

次のステップ

これで、Flash Player のメモリモデルと Scout のメモリのプロファイリング機能について詳しく知ることができましたので、より効果的にコンテンツ内に潜むメモリの問題を特定しトラッキングできるようになりました。Flash Player の動作方法について詳しくは、Flash Player および Adobe AIR のガベージコレクションの内部的な仕組みAdobe Scout による Flash Player の把握の記事をお読みください。

現在、新しいコメントシステムに移行中であるため、コメントの投稿は受け付けておりません。しばらくの間、ご意見やご感想をお寄せいただく場合は、フィードバックフォームをご利用ください。ご迷惑をおかけしますが、どうぞよろしくお願い申し上げます。