JavaのGC動作原理

5503 ワード

GCの基本原理
Javaのメモリ管理は、実際にはオブジェクトの割り当てと解放を含むオブジェクトの管理です.
プログラマにとって、割り当てオブジェクトはnewキーワードを使用します.オブジェクトを解放するとき、オブジェクトのすべての参照をnullに割り当て、プログラムがこのオブジェクトにアクセスできない限り、オブジェクトを「不可」と呼びます.GCは、すべての不可」オブジェクトのメモリ領域を回収します.
GCの場合、プログラマがオブジェクトを作成すると、GCはそのオブジェクトのアドレス、サイズ、使用状況を監視し始めます.通常、GCは、ヒープ(heap)内のすべてのオブジェクトを図面に記録して管理する(詳細は参考資料1参照).このようにして、どのオブジェクトが「到達可能」であり、どのオブジェクトが「到達不可能」であるかを決定します.GCがいくつかのオブジェクトが「到達不可能」であると決定した場合、GCはこれらのメモリ領域を回収する責任があります.しかし、GCが異なるプラットフォームで実現できることを保証するために、Java規範はGCの多くの行為に対して厳格な規定を行っていない.例えば,どのような種類の回収アルゴリズムを採用するか,いつ回収するかなどの重要な問題については明確な規定はない.したがって,異なるJVMの実装者には異なる実装アルゴリズムがあることが多い.これはJavaプログラマーの開発にも不確実性をもたらします.本論文では,GC作業に関連するいくつかの問題を検討し,この不確実性がJavaプログラムに与える負の影響を低減するために努力した.

  • インクリメンタルGC(IncrementalGC)

  • GCは、JVMにおいて通常、1つまたは複数のプロセスによって実現され、それ自体もユーザプログラムと同様にheap空間を占有し、実行時にもCPUを占有する.GCプロセスが実行されると、アプリケーションは実行を停止する.したがって、GCの実行時間が長い場合、ユーザはJavaプログラムの停止を感じることができる一方、GCの実行時間が短すぎると、オブジェクトの回収率が低すぎる可能性があり、これは、回収すべきオブジェクトが回収されず、依然として大量のメモリを消費していることを意味する.したがって,GCを設計する際には,ストップ時間と回収率の間でバランスをとる必要がある.良いGC実装では、ユーザーが自分の必要な設定を定義することができます.例えば、メモリにはデバイスが限られており、メモリの使用量に非常に敏感で、GCがメモリを正確に回収することを望んでいます.プログラムの速度の低下を気にしません.他のリアルタイムのネットゲームでは、プログラムが長時間中断されることは許されません.インクリメンタルGCとは,一定の回収アルゴリズムにより,1つの長時間の割り込みを複数の小さな割り込みに分割することで,GCがユーザプログラムに与える影響を低減することである.インクリメンタルGCは、全体的な性能上、通常のGCほど効率的ではないかもしれませんが、プログラムの最長停止時間を短縮できます.
    SunJDKが提供するHotSpotJVMは、インクリメンタルGCを使用しないインクリメンタルGCをサポートします.インクリメンタルGCを開始するには、Javaプログラムを実行するときに-Xincgcのパラメータを増やす必要があります.HotSpotJVMインクリメンタルGCの実装はTrainGCアルゴリズムを採用している.基本的な考え方は、スタック内のすべてのオブジェクトを作成と使用状況に応じてグループ化(階層化)し、頻繁に使用され、相関性のあるオブジェクトを一列に配置し、プログラムの実行に伴ってグループを調整することです.GCが実行されると、常に最も古い(最近アクセスが少ない)オブジェクトを回収し、グループ全体が回収可能なオブジェクトである場合、GCはグループ全体を回収します.このように,GC実行ごとに一定割合の到達できないオブジェクトのみを回収し,プログラムの円滑な実行を保証する.
    finalize関数の詳細
    finalizeはObjectクラスにあるメソッドで、このメソッドのアクセス修飾子はprotectedであり、すべてのクラスがObjectのサブクラスであるため、ユーザークラスはこのメソッドにアクセスしやすい.finalize関数はチェーン呼び出しを自動的に実装しないため、手動で実装する必要があります.そのため、finalize関数の最後の文は通常super.finalize()です.このようにして、finalizeの呼び出しを下から上へ実現することができます.すなわち、自分のリソースを解放してから、親のリソースを解放します.
    Java言語仕様によると、JVMはfinalize関数を呼び出す前に、このオブジェクトが到達できないことを保証しますが、JVMはこの関数が必ず呼び出されることを保証しません.また、仕様はfinalize関数を最大1回実行することを保証します.
    多くのJava初心者は、この方法がCの構造関数に似ていると考え、多くのオブジェクト、リソースの解放をこの関数に入れています.実は、これは良い方法ではありません.理由は3つあるが,GCはfinalize関数をサポートするために,この関数を上書きするオブジェクトに多くの付加的な作業を行う.二つ目は、finalizeの実行が完了すると、オブジェクトが到達可能になる可能性があり、GCはオブジェクトが到達可能かどうかをもう一度チェックしなければならない.このためfinalizeを使用するとGCの動作性能が低下します.三つ目は、GCがfinalizeを呼び出す時間が不確定であるため、このような方法でリソースを解放することも不確定である.
    通常、finalizeは、制御が容易ではなく、I/Oの動作、データの接続など、非常に重要なリソースの解放に使用されます.これらのリソースの解放は、アプリケーション全体にとって非常に重要です.この場合、プログラマは、プログラム自体によってこれらのリソースを管理(解放を含む)することを主とし、finalize関数によってリソースを解放する方法を補助とし、finalizeだけに頼ってリソースを解放するのではなく、二重保険の管理メカニズムを形成しなければならない.
    次の例では、finalize関数が呼び出された後も、到達可能である可能性があり、オブジェクトのfinalizeが1回しか実行できないことを示します.
    MyObjectオブジェクトはfinalizeで到達可能なオブジェクトになりますが、finalize関数は最大1回しか呼び出されないため、次回の回収ではfinalizeは呼び出されません.
    プログラムがGCとどのように対話するか
    Java 2はメモリ管理機能を強化し、java.lang.refパッケージを追加し、3つの参照クラスを定義しました.これら3つの参照クラスは、それぞれSoftReference、WeakReference、PhantomReferenceeである.これらの参照クラスを使用することで、プログラマはGCとある程度対話することができ、GCの生産性を改善することができる.これらの参照クラスの参照強度は、到達可能オブジェクトと到達不可能オブジェクトの間にあります.
    リファレンスオブジェクトを作成するのも簡単です.たとえば、SoftReferenceオブジェクトを作成する必要がある場合は、まずオブジェクトを作成し、通常のリファレンス方式(到達可能オブジェクト)を採用します.次に、オブジェクトを参照するSoftReferenceを作成します.最後に、通常の参照をnullに設定します.これにより、このオブジェクトにはSoftReference参照が1つしかありません.また、このオブジェクトをSoftReferenceオブジェクトと呼びます.
    SoftReferenceの主な特徴は、引用機能が強いことです.このようなメモリは、メモリが足りない場合にのみ回収されるため、メモリが十分な場合には回収されません.また、これらの参照オブジェクトは、JavaがOutOfMemory異常を投げ出す前にnullに設定されることを保証することもできる.これは、OutOfMemoryを引き起こさずに、以下に示すような参照クラス型の使用擬似コードを与えることなく、Cacheの機能を実現するために使用することができる.//  
    Imageimage=newImage();// Image  
    … 
    // image 
    … 
    // image, soft , ; 
    SoftReferencesr=newSoftReference(image); 
    image=null; 
    … 
    //  
    if(sr!=null)image=sr.get(); 
    else{ 
    // GC , image, ; 
    image=newImage(); 
    sr=newSoftReference(image); 
    }
    Weak Soft :GC , Soft , Weak ,GC 。Weak 、 GC 。 ,GC Weak , Weak GC 。Weak Map , , null ,GC 。
    Phantom参照の用途は少なく,主にfinalize関数の使用を支援するために用いられる.Phantomオブジェクトとは、finalize関数を実行したオブジェクトを指し、到達できないオブジェクトですが、GCで回収されていません.このオブジェクトはfinalizeのいくつかの後期の回収作業を支援することができ,Referenceのclear()メソッドを上書きすることによって,リソース回収メカニズムの柔軟性を強化した.
    Javaエンコーディングの推奨事項
    GCの動作原理に基づいて、いくつかのテクニックと方法を通じて、GCをより効率的に実行し、アプリケーションの要求に合致させることができます.以下はいくつかのプログラム設計のいくつかの提案です.
    1.最も基本的なアドバイスは、不要なオブジェクトの参照をできるだけ早く解放することです.ほとんどのプログラマは、一時変数を使用する場合、参照変数をアクティブドメイン(scope)を終了した後にnullに自動的に設定します.この方法では、配列、キュー、ツリー、図など、複雑なオブジェクト図に特に注意する必要があります.これらのオブジェクトの間に相互参照関係が複雑です.このようなオブジェクトの場合、GCはそれらを回収するのに一般的に効率が低い.プログラムが許可すれば、不要な参照オブジェクトをnullにできるだけ早く割り当てることで、GCの作業を加速させることができます.
    2.finalize関数はなるべく使わないでください.finalize関数は、Javaがプログラマにオブジェクトまたはリソースを解放する機会を提供します.しかし、GCの作業量を増やすため、finalize方式で資源を回収することはできるだけ少なくします.
    3.頻繁に使用する画像を使用する必要がある場合は、ソフトウェアアプリケーションタイプを使用します.OutOfMemoryを起こさずにプログラム呼び出しのためにできるだけ画像をメモリに保存することができます.
     
    4.配列、ツリー、図、チェーンテーブルなどのデータ構造を含む集合データ型に注意してください.これらのデータ構造はGCにとって回収がより複雑です.さらに,いくつかのグローバル変数,およびいくつかの静的変数に注意する.これらの変数は、サスペンションオブジェクト(danglingreference)を引き起こしやすく、メモリの無駄になります.
    5.プログラムに一定の待ち時間がある場合、プログラマは手動でSystem.gc()を実行し、GCに実行を通知することができるが、Java言語規範はGCが必ず実行することを保証しない.インクリメンタルGCを使用するとJavaプログラムの一時停止時間を短縮できます.