KC Blog

[Android][Memory]記憶体最適化+GC管理関連概念の共有

7 min read
AndroidDev#Android#Kotlin

今日のこの投稿

メモを書く方法を通じて

私が理解しているAndroidのメモリ管理について記録しようと思います

ここでは同じメモに継続的に更新する予定です

もしもっとAndroidのメモリ管理知識を読んだら

それを一つの投稿に集中させたいと思います

Android Memory Note

heap:

Android仮想マシンはheap内のメモリ割り当てを継続的に追跡します

heapはシステムが割り当てたjava/kotlinオブジェクトを格納するためのメモリ領域です

garbage collection(gc):

その目的は以下を達成するためです:

  • 使用されていないオブジェクトを探す
  • これらのオブジェクトが使用しているメモリを回収し、heapに返す

マルチタスク環境では

Androidは各heapのサイズを制限します

このサイズはAndroidデバイスの利用可能なRAMの量に基づいて決定されます

さらに

heap容量がいっぱいになると

システムがまだメモリを割り当てようとすると

OutOfMemoryErrorが発生する可能性があります

Frequent Garbage Collection

以前、海外の記事でGCをmemory churnとも呼んでいました

言い換えれば

GCは通常、短時間でメモリが必要なときに発生します

heap空間が不足しているため

アプリにheapを割り当てる必要があると同時に

heap空間を解放して不足を補う必要があります

したがって、頻繁にGCが発生するとメモリ関連の問題が発生する可能性があります

例を挙げると:

同時に

アプリが大量のメモリ空間をオブジェクトの作成に割り当てる必要がある場合

しかしheap空間が不足しているため

GCがトリガーされてheap空間を回収します

しかし、この繰り返しの過程で

アプリがフリーズすることがあります

この時点では通常、oomは表示されません

しかし、フリーズやクラッシュが発生し

ユーザー体験が悪化します

コード付きの例を挙げると:

これは一般的なRecyclerViewの実装アダプタです

その中のbind()は新しいデータが生成されるときのロジックを実現するためのものです

val demoBitmap = BitmapFactory.decodeResource(itemView.context.resources, R.drawable.bg_demo_photo)

このbind()内には固定の画像をitemに読み込むコードがあります

ここに置くと

bindされるたびにitemが再度bitmap画像を読み込むことになります

少量の画像や小規模なプロジェクトでは違いを感じないかもしれませんが

大量に繰り返し読み込む場合や

itemが100個、1000個ある場合

それぞれが繰り返し読み込むと

heap容量の消費が非常に大きくなります

したがって、最も簡単な方法は固定のものを一度だけ読み込むように変更することです

または、サードパーティのライブラリを使用して画像をキャッシュに保存し

読み込み回数を減らすこともできます

もちろん、キャッシュを使用するとOOMが発生する可能性もあるため

特定の条件下でキャッシュをクリアする必要があります

プロジェクトで遭遇する問題に応じて最適化できます

Android Memory Leak

gcが解放できないオブジェクトの参照漏れ

それはどこかでこの参照がまだ必要だと考えているためです

このような状況は通常、メモリリークと呼ばれます

Inner Classes : 内部クラスと外部クラスが参照を持つと、メモリリークが発生する可能性があります

例:

上記のコードのように

内部クラスが外部のshowResultを参照しているため

AsyncTaskがバックグラウンドで実行されると

activityが終了しても

AsyncTaskがまだ実行されている可能性があり

そのためメモリリークが発生する可能性があります

問題を解決するためのアプローチ

外部クラスのメソッド呼び出しを削除する

または他の方法で外部クラスにアクセスする

例えば弱参照を使用する

弱参照を使用しても外部クラスにアクセスできます

しかし、強参照のように強力ではないため、メモリ内に持続的に保持されることはありません

ガベージコレクタがオブジェクトの強参照を見つけられない場合、それを探してnullに設定します

匿名クラス: 一部の匿名クラスは外部クラスよりも長く生存することがあります

これがメモリリークを引き起こします

静的変数: companion objectやstaticを使用してクラスを修飾すると

オブジェクトが最初からロードされ

その後解放されない

これがメモリリークを引き起こします

例えば static activity