【Kotlin/Android】WeakReferenceの使い方!弱参照とメモリの確保/解放

この記事からわかること
- Android Studio/KotlinのWeakReference(弱参照)とは?
- 強参照との違い
- メモリの確保と解放
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Flamingo
- Kotlin:1.8.20
WeakReferenceとは?
KotlinのWeakReference
はオブジェクトへの参照を弱参照にするためのクラスです。通常オブジェクトは強参照でメモリアドレスと関連づいています。これを明示的に弱参照にすることでガーベジコレクションによるメモリの解放対象となり、メモリリークの発生を抑制することが可能になっています。
WeakReferenceの使い方の前にメモリの確保と解放の流れを理解しておきます。
メモリの確保
メモリが確保されるのはクラスのインスタンスを生成した時などです。そこからプロパティや変数に格納されるとそのメモリ(インスタンス)の参照という形で紐付けがカウントされます。この参照を強参照と呼びます。
data class Person (
var name: String
)
// 強参照でメモリと紐付け
var person1: Person? = Person("ame")
メモリの解放
メモリが解放されるのはオブジェクトへの参照がなくなった時です。具体的には変数にnullを格納して参照を解放した場合や、メソッドのスコープから外れてそのオブジェクトへの参照がなくなった場合などです。
// 変数にnullを格納
person1 = null
// personはこのメソッドのスコープ内でのみ有効のローカル変数
fun someFunction() {
val person = Person("ame")
} // someFunctionを抜けるとpersonへの参照がなくなる
メモリが解放されない
逆に言うとオブジェクトへの参照がなくならないとメモリは解放されません。Person
オブジェクトをコピーして変数に格納するとメモリの参照カウントは2
になります。この状態でperson1
にnull
を格納してもperson2
からオブジェクトに参照できるのでメモリは解放されず、person2
の参照がなくなった場合にメモリが解放されます。
var person1: Person? = Person("ame")
var person2 = person1
println("Before BC: ${person1?.name}") // Before BC: ame
println("Before BC: ${person2?.name}") // Before BC: ame
// 強参照をnullにする
person1 = null
println("Before BC: ${person1?.name}") // Before BC: null
println("Before BC: ${person2?.name}") // Before BC: ame
// ガベージコレクションを実行
System.gc()
println("After GC: ${person1?.name}") // After BC: null
println("After BC: ${person2?.name}") // After BC: ame
// 強参照をnullにする(ここでメモリが解放される)
person2 = null
ガーベジコレクションを明示的に実行してもメモリは解放されません。メモリ消費量の確認やSystem.gc()
などについては以下の記事を参考にしてください。
WeakReferenceで弱参照にする
先ほどのようにコピーをたくさん作成するとその数だけ片付けをしないとメモリが解放されず、メモリリークを引き起こす原因になります。これを解消できるのがWeakReference
です。WeakReference
のコンストラクタに対象のオブジェクトを渡し、WeakReference
でラップした状態にします。値を取得する際はget
メソッドを使用します。
メモリが解放されないで紹介したコードをWeakReference
に置き換えると結果が異なりガベージコレクションが発生した後にはメモリが解放されていることがわかります。
var person1: Person? = Person("ame")
val person2 = WeakReference(person1)
println("Before BC: ${person1?.name}") // Before BC: ame
println("Before BC: ${person2.get()?.name}") // Before BC: ame
// 強参照をnullにする
person1 = null
println("Before BC: ${person1?.name}") // Before BC: null
println("Before BC: ${person2.get()?.name}") // Before BC: ame
// ガベージコレクションを実行
System.gc()
println("After GC: ${person1?.name}") // After BC: null
println("After GC: ${person2.get()?.name}") // After BC: null
// person2を明示的にnullにしなくてもガベージコレクションの対象になり解放される
// person2 = null
clearメソッド
clear
メソッドを実行するとオブジェクトへの参照をクリアすることができます。
person2.clear()
println("Before BC: ${person2.get()?.name}") // null
メモリリークを検知できるライブラリ
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。