Java 8 での到達可能なオブジェクトの JVM ファイナライズ: 探索
finalize() の使用を妨げるベスト プラクティスに従っているにもかかわらず、 Java 7 から Java 8 への最近のアップグレードにより、予期せぬ問題が明らかになりました。このシナリオでは、現在のスレッドのスタックによって依然として強力に到達可能なオブジェクトが Java 8 ランタイムでファイナライズされます。
コードと例外について
問題のコードにはカスタム MIME ライブラリが含まれており、MIMEBodyPart クラスは HTTPMessage を拡張します。 HTTPMessage は、関連する入力ストリームを閉じようとする Finalize() メソッドを実装しており、アクティブな writePart() 操作中にストリームがすでに閉じられている場合は例外が発生します。
原因を調査中
予期せぬ最終結果に困惑した開発者は、コードと JVM の動作をさらに詳しく調べました。 MIMEBodyPart オブジェクトは現在のスレッドのスタックから確かに到達可能であったため、ファイナライズされるべきではなかったことが判明しました。
到達不能の可能性を仮説
にもかかわらずオブジェクトの見かけの到達可能性は明らかですが、Java 仮想マシン (JVM) はオブジェクトを依然として次のように認識できると理論化されました。後続のコードで明示的に参照されていない場合は到達できません。この「到達不能能力」の概念は、スタック上のアクティブなメソッド呼び出しにも拡張されます。
到達不可能性を示す例
この動作を説明するために、簡略化されたコード例が提示されました。
class FinalizeThis { @Override protected void finalize() { System.out.println("finalized!"); } void loop() { System.out.println("loop() called"); for (int i = 0; i < 1,000,000,000; i++) { if (i % 1,000,000 == 0) System.gc(); } System.out.println("loop() returns"); } public static void main(String[] args) { new FinalizeThis().loop(); } }
この例では、loop() メソッドにガベージ コレクションをトリガーする大規模なループが含まれています定期的に。ループ メソッドが FinalizeThis オブジェクトのインスタンス メソッドを積極的に呼び出しているにもかかわらず、明らかに到達不能なため、JVM はオブジェクトをファイナライズし、ガベージ コレクションを行います。
元のシナリオへの仮説の適用
MIMEBodyPart オブジェクトの場合も同様の状況が発生する可能性があると推測されました。その後参照せずにローカル変数に保存された場合、到達不能になり、ファイナライズの影響を受けやすくなる可能性があります。
更新と追加の観察
さらなるテストと分析を通じて、 JIT コンパイラがこの動作に役割を果たしていることが明らかになりました。実行前にメソッドを強制的に JIT コンパイルすること (-Xcomp オプション) により、早期終了の問題が再浮上しました。これは、単純化された例での最初のファイナライズの欠如は、より積極的な到達可能性分析を実行するコンパイルではなく解釈によるものであることを示唆しています。
結論
問題の詳細は正確なコード構造と実行環境によって異なる可能性がありますが、到達不能と認識されたために到達可能なオブジェクトであってもファイナライズされる可能性があるという基礎的な概念は注目に値します。これは、オブジェクトの到達可能性と、アクティブなオブジェクトに対するファイナライゼーション呼び出しの潜在的な結果を理解することの重要性を強調しています。
以上が`finalize()` を妨げるベスト プラクティスにもかかわらず、到達可能な Java オブジェクトが Java 8 でファイナライズされるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。