【Kotlin/Android】Memory Profilerの使い方!コードでメモリ消費量を取得するには?

この記事からわかること
- Android Studio/KotlinのMemory Profilerの使い方
- CPU/Memory/Energyの使用量するには?
- コードからメモリ消費量を取得するには?
- Runtime.getRuntimeのtotalMemoryとfreeMemoryの使い方
- ガーベジコレクションをコードから実行する方法
- OOM(OutOfMemory)対策
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
Android Profiler
公式リファレンス:Memory Profiler を使用してアプリのメモリ使用量を調べる
起動しているアプリのメモリ消費量を確認できる「Memory Profiler」はAndroid Studioの機能の「Android Profiler」から提供されている1つで、アプリのメモリ消費量などを確認することができます。これは実機へビルドしてテストしている場合に利用することができます。
Android Profiler
- CPU Profiler・・・CPUの使用量(パフォーマンス)
- Memory Profiler・・・メモリの消費量(割り当て量)
- Energy Profiler・・・バッテリーの消耗につながる可能性があるエネルギー使用量
Android Profilerを起動させるには上部メニュー「View」 > 「Tool Windows」 > 「Profiler」をクリックします(Android Studioボトムバーの「Profiler」からでも表示できます)。起動させると以下のように上からCPU/Memory/Energyの使用量が可視化されて表示されるようになります。

メモリリークが発生している場合はこのMemoryの使用量が減らずにどんどん増えていくような挙動になります。
Memory Profiler
メモリ部分をクリックすると「Memory Profiler」が起動します。表示されているメモリは7種類のカテゴリに分けられてスタックされます。またゴミ箱アイコンをクリックすることで明示的にガーベジコレクションを実行させることが可能です。

Others
アプリによって使用された、カテゴリが不明なメモリ。
Code
dexバイトコード、最適化またはコンパイルされた dex コード、.so ライブラリ、フォントなど、コードとリソースのためにアプリが使用したメモリ。
Stack
アプリのネイティブスタックとJavaスタックの両方によって使用されたメモリ。通常、アプリが実行しているスレッドの数に関係する。
Graphics
GLサーフェスやGLテクスチャなど画面にピクセルを表示するためにグラフィックバッファキューに使用されたメモリ。
Native
C / C++ のコードから割り当てられたオブジェクトのメモリ。
※コードを Java または Kotlin で記述していても、画像アセットやグラフィックを処理など一部の実装はネイティブメモリを使用するためC / C++での記述がなくても表示される
Java
Java / Kotlin のコードから割り当てられたオブジェクトのメモリ。
Allocated
アプリによって割り当てられた Java / Kotlin オブジェクトの数。C または C++ で割り当てられたオブジェクトはカウントされない。
Kotlinでメモリ消費量を取得する
Kotlinのコードから現在消費しているメモリ量を取得するにはRuntime
クラスを使用します。totalMemory
で割り当て済みのメモリをfreeMemory
で空きメモリ量を取得することが可能です。
fun getMemoryUsage() {
val runtime = Runtime.getRuntime()
val totalMemory = runtime.totalMemory() // 現在の割り当て済みメモリ量
val freeMemory = runtime.freeMemory() // 現在の空きメモリ量
val usedMemory = totalMemory - freeMemory // 使用中のメモリ量
Log.d("Memory", "メモリ消費量:${usedMemory}") // メモリ消費量:15200776
}
メモリ量を取得する
端末のメモリ量はmaxMemory
メソッドで取得できます。
fun getMemoryUsage() {
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory()
Log.d("Memory", "メモリ量:${maxMemory}") // メモリmax:201326592
}
ActivityManager
からも取得できるようです。
val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val largeMemory = am.largeMemoryClass
Log.d("Memory", "メモリ量:${largeMemory}") // メモリ量:512
メモリの確保や解放のタイミングや強参照/弱参照については以下の記事を参考にしてください。
コードからガーベジコレクションを実行する
Kotlinのコードからガーベジコレクションを実行するにはSystem.gc()
を使用します。ですがこれはガベージコレクタに対してガベージコレクションの実行要求を送るだけであり、すぐさまガベージコレクションが実行されることは保証されないので注意してください。
System.gc()
OOM対策でヒープ領域を増やす
処理が非常に重くメモリを大量消費するアプリではOOM(OutOfMemory)エラーが発生することが多くなります。基本的にはアプリのパフォーマンスを改善する必要がありますが、どうしても改善しない場合の苦肉の策としてヒープ領域を拡大する設定がありました。
マニフェストファイルのandroid:largeHeap
属性にtrue
を渡すことでヒープ領域を拡大することができるようです。
<application
android:largeHeap="true" >
</appplication>
ただし設定することでガーベジコレクションに時間がかかるようになるなどデメリットもあるようなので使い所には注意が必要です。
What are the downsides of using android:largeHeap="true"?
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。