1. ホーム
  2. java

[解決済み] Javaで変数にfinalを使用するとガベージコレクションが改善されますか?

2023-05-13 05:03:26

質問

今日、同僚と一緒に final キーワードの使用について議論しました。

例えば、こんなメソッドを書いたとします。

public Double doCalc(final Double value)
{
   final Double maxWeight = 1000.0;
   final Double totalWeight = maxWeight * value;
   return totalWeight;  
}

メソッド内の変数を宣言する final で宣言しておくと、メソッド終了後にガベージコレクションがメソッド内の未使用の変数からメモリを一掃するのに役立ちます。

これは本当でしょうか?

どのように解決するのですか?

ここでは、最終値型のローカル変数ではなく、最終参照型のフィールドを使った少し変わった例を紹介します。

public class MyClass {

   public final MyOtherObject obj;

}

MyClass のインスタンスを作成するたびに、MyOtherObject インスタンスへの外部参照を作成することになり、GC はライブオブジェクトを探すためにそのリンクをたどらなければならなくなるのです。

JVMはマークスイープGCアルゴリズムを使用し、GC "root"ロケーション(現在のコールスタック内のすべてのオブジェクトのように)内のすべてのライブリファレンスを検査しなければなりません。各ライブオブジェクトは生きているとして"mark"され、ライブオブジェクトによって参照される任意のオブジェクトはまた生きているとしてマークされます。

マーク段階の完了後、GC はヒープを一掃し、マークされていないすべてのオブジェクトのメモリを解放します (そして、残りの生きているオブジェクトのメモリをコンパクトにします)。

また、Javaヒープメモリがquot;若い世代とquot;古い世代に分割されることを認識することが重要です。すべてのオブジェクトは、最初に若い世代に割り当てられます(「苗床」とも呼ばれます)。ほとんどのオブジェクトは短命なので、GCは若い世代から最近のゴミをより積極的に解放します。オブジェクトが若い世代の収集サイクルを生き残った場合、それは古い世代 (時として "tenured generation") に移動され、処理頻度が低くなります。

つまり、頭から、"いいえ、'final' modifer は GC の作業負荷を減らすのに役立ちません" と言うつもりです。

私の意見では、Java でメモリ管理を最適化するための最良の戦略は、偽の参照を可能な限り迅速に排除することです。それを行うには、使用を終えたらすぐにオブジェクトの参照に "null" を代入することです。

あるいは、もっと良い方法は、各宣言スコープのサイズを最小にすることです。例えば、1000行のメソッドの冒頭でオブジェクトを宣言し、そのメソッドのスコープ(最後の閉じ中括弧)が閉じるまでオブジェクトが生き続ける場合、オブジェクトは実際に必要な時間よりもはるかに長く生き続けるかもしれません。

もし、十数行のコードしかない小さなメソッドを使用するなら、そのメソッド内で宣言されたオブジェクトはより迅速にスコープから外れ、GC ははるかに効率的な若い世代の中でその仕事のほとんどを行うことができます。絶対に必要でない限り、オブジェクトを古い世代に移動させたくはないでしょう。