Android プラットフォームでは、システムはできるだけ多くのシステムメモリ(RAM)を使用するよう試み、必要に応じてさまざまなメモリ最適化を行い、空き容量を確保します。こうした最適化はゲームに悪影響(速度が低下する、完全に強制終了するなど)を及ぼすことがあります。こうした最適化の詳細については、プロセス間のメモリ割り当てをご覧ください。
このページでは、ゲームに影響を及ぼすメモリ不足状態を回避するための手順について説明します。
onTrimMemory() に応答する
このシステムは
onTrimMemory()
ライフサイクル イベントをアプリに通知する方法を
メモリの使用量を自発的に減らし、
ローメモリ キラー(LMK)
メモリを解放して他のアプリで使用できるようにします
アプリがバックグラウンドで強制終了された場合、次回ユーザーが起動したときに 動作が遅くなるため コールド スタート。ユーザーの メモリ消費量が 60% に達することが 説明します。
trim イベントに応答するときは、大きなメモリ割り当てを解放する
すぐには必要ではなく、オンデマンドで再構築できるものです。対象
(例: ローカルからデコードされたビットマップのキャッシュがアプリにある場合)
画像が圧縮されている場合は、そのファイルを
キャッシュへの保存が
TRIM_MEMORY_UI_HIDDEN
。
Kotlin
class MainActivity : AppCompatActivity(), ComponentCallbacks2 { override fun onTrimMemory(level: Int) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Java
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 { public void onTrimMemory(int level) { switch (level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } } }
C#
using UnityEngine; using System.Collections; using System.Collections.Generic; class LowMemoryTrigger : MonoBehaviour { private void Start() { Application.lowMemory += OnLowMemory; } private void OnLowMemory() { // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets()) } }
Memory Advice API ベータ版を使用する
Memory Advice API は、onTrimMemory に代わり、はるかに高い再現性と精度で、差し迫っている LMK を予測する API として開発されました。API はこれを達成するために、
メモリリソースを監視して、特定のリソースにアクセスしたときに
超えています。この API は、Google Cloud の
アプリに直接影響します。Memory Advice API は
の代替
onTrimMemory
メモリ管理を目的としています。
Memory Advice API を使用するには、スタートガイドをご覧ください。
控えめなメモリ配分にする
メモリを控えめに配分して、メモリ不足を回避します。検討すべき項目には次のようなものがあります。
- 物理 RAM のサイズ: ゲームは多くの場合、デバイスの物理 RAM 量の 1/4 から 1/2 を使用します。
- 最大 zRAM サイズ: zRAM が多いほど、ゲームで割り当てることができるメモリも多くなる可能性があります。この量はデバイスによって異なります。この値は、
/proc/meminfo
でSwapTotal
を探すと見つかります。 - OS のメモリ使用量: システム プロセスに多くの RAM を割り当てるデバイスでは、ゲームに割り当てられるメモリが少なくなります。システムは、システム プロセスを強制終了する前にゲームのプロセスを強制終了します。
- インストール済みのアプリのメモリ使用量: 多数のアプリがインストールされたデバイスでゲームをテストします。ソーシャル メディア アプリとチャットアプリは常に動作する必要があり、メモリの空き容量に影響を及ぼします。
控えめなメモリ配分を実現できない場合は、より柔軟なアプローチを取ります。システムでメモリ不足の問題が発生した場合は、ゲームが使用しているメモリ量を減らします。たとえば、onTrimMemory()
に応じて低解像度のテクスチャを割り当てるか、格納するシェーダーを減らします。このメモリ割り当ての動的アプローチでは、特にゲーム設計フェーズで、デベロッパーの作業が増えることになります。
スラッシングを回避する
スラッシングは、空きメモリは少ないが、ゲームを強制終了するほどではない場合に発生します。このような状況では、ゲームにまだ必要なページが kswapd
によって回収されているため、メモリからページを再読み込みしようとします。空き容量が不足しているため、ページはスワップアウトされ続けます(連続スワップ)。システム トレースはこの状況を、kswapd
が継続的に実行されるスレッドとして報告します。
スラッシングの症状として、フレーム時間が長いことが挙げられます。1 秒以上の場合もあります。この状況を解決するには、ゲームのメモリ使用量を減らします。
利用可能なツールを使用する
Android には、システムがメモリを管理する仕組みについて理解する際に役立つツールが用意されています。
Meminfo
このツールは、メモリの統計情報を収集して、割り当てられた PSS メモリの容量とメモリが使用されたカテゴリを表示します。
次のいずれかの方法で meminfo 統計情報を出力します。
adb shell dumpsys meminfo package-name
コマンドを使用する。- Android Debug API から
MemoryInfo
呼び出しを使用する。
PrivateDirty
統計情報は、ディスクにページングできず他のプロセスと共有されていない、プロセス内の RAM の容量を示します。プロセスが強制終了されると、この容量の大部分をシステムで利用できるようになります。
メモリ トレースポイント
メモリ トレースポイントは、ゲームが使用している RSS メモリの量を追跡します。RSS メモリ使用量の計算は、PSS 使用量の計算よりはるかに高速です。計算が高速であるため、RSS はメモリサイズの変化をより詳細に示し、ピーク時のメモリ使用量をより正確に測定します。そのため、ゲームのメモリ不足を引き起こす可能性のあるピークに気づきやすくなります。
Perfetto と長いトレース
Perfetto は、デバイスのパフォーマンスとメモリの情報を収集し、ウェブベースの UI に表示するためのツールのスイートです。任意の長いトレースをサポートしているため、RSS の経時的な変化を確認できます。オフライン処理用に生成したデータに対して SQL クエリを発行することもできます。System Tracing アプリから長いトレースを有効にします。トレースに対して memory:Memory カテゴリが有効になっていることを確認します。
heapprofd
heapprofd
は、Perfetto の一部であるメモリ トラッキング ツールです。このツールは、malloc
を使用してメモリが割り当てられた場所を示すことで、メモリリークの発見に役立ちます。heapprofd
は Python スクリプトを使用して起動できます。このツールはオーバーヘッドが少ないため、他のツール(Malloc Debug など)のようにパフォーマンスに影響することはありません。
bugreport
bugreport
は、ゲームがメモリ不足でクラッシュしたのかどうかを調べるロギングツールです。ツールから出力されるレポートは、logcat を使用するよりもはるかに詳細です。ゲームがメモリ不足でクラッシュしたのか、LMK によって強制終了されたのかが示されるため、メモリのデバッグに役立ちます。
詳細については、バグレポートのキャプチャと確認をご覧ください。