ご存知のように、上記の回答は、コンパニオンオブジェクトがクラスに変換され、それらを宣言するクラスがコンパニオンクラスのオブジェクトへの静的参照を保持していることも明らかにしています。
public static final MyClass.Companion Companion = new MyClass.Companion(null);
今問題
コンパニオンオブジェクトはアプリのライフサイクルの間メモリに残りますか
宣言クラスがstatic
コンパニオンクラスへの参照を保持しているため、質問static
はjvm のフィールドの存続期間に短縮され、class
答えはJVM仕様にありますが、仕様は説明が少々乾燥しているため、内部の本からいくつかのスニペットを追加していますJava仮想マシン。
あなたの例のようにclass
、1つのコンパニオンオブジェクトだけを持っているとします。
最初の質問は、いつコンパニオンクラスのオブジェクトが作成されるかです。 またはstatic
フィールドが初期化されるとき?
関連するテキストブック。(コンテキストの本はクラスのロード手順について話している)
初期化
最初のアクティブな使用のためにクラスまたはインターフェースを準備するために必要な最後のステップは、初期化、つまりクラス変数を適切な初期値に設定するプロセスです。ここで使用する「適切な」初期値とは、クラス変数に対してプログラマーが希望する開始値です。適切な初期値は、準備中にクラス変数に与えられるデフォルトの初期値と対照的です。上記のように、仮想マシンは各変数のタイプのみに基づいてデフォルト値を割り当てます。対照的に、適切な初期値は、プログラマーだけが知っているいくつかのマスタープランに基づいています。Javaコードでは、クラス変数初期化子または静的初期化子を介して適切な初期値が指定されています。
したがってMyClass
、ロードして初期化すると、コンパニオンクラスのオブジェクトが作成されることがわかります。
しかし、JVMがロードされる原因は何MyClass
ですか?
Java Virtual Machine仕様は、クラスとインターフェースのロードおよびリンクのタイミングに実装の柔軟性を提供しますが、初期化のタイミングを厳密に定義します。すべての実装は、最初のアクティブな使用時に各クラスとインターフェイスを初期化する必要があります。クラスのアクティブな使用法は次のとおりです。
クラスの新しいインスタンスでのコンストラクターの呼び出し
クラスを要素型とする配列の作成
クラスによって宣言されたメソッドの呼び出し(スーパークラスから継承されていない)
4クラスによって宣言されたフィールドの使用または割り当て(スーパークラスまたはスーパーインターフェースから継承されない)。ただし、静的で最終的なフィールドであり、コンパイル時の定数式によって初期化されるフィールドを除く
したがって、4番目のポイントのようにMyClass.foo()
、kotlinから実行するかMyClass.Companion.foo()
、この時点MyClass
でロードされ、準備が整います。(おそらくかなり早い)
この時点ではオブジェクトはMyClass
存在しないことに注意してください。つまり、式は使用していませんMyClass()
。
これはstatic
、アプリケーションが実行されている限り、フィールドがメモリに残ることを意味しますか?
宣言型がアンロードMyClass
されると、ガベージコレクションされる可能性があります。この場合、JVMまたはART(androidの場合)がアンロードされると、コンパニオンオブジェクトがガベージコレクションされる可能性があります。
JVM Specはクラスのアンロードについて次のように述べています
Javaプログラミング言語の実装により、クラスがアンロードされる場合があります。
§12.6で説明されているように、ガベージコレクターがその定義クラスローダーを再利用できる場合に限り、クラスまたはインターフェースをアンロードできます。
ブートストラップローダーによってロードされたクラスとインターフェースは、アンロードされない場合があります。
実用的なクラスでは、アンロードはほとんど(ほとんど言っていませんが)発生しないので、コンパニオンオブジェクトはアプリのライフサイクルの間メモリに残ります。