Javaのステップアップ--ゴミの回収と終了処理の概要


最初のプログラミング言語に触れてから、変数の初期化が重要であることを強調し続けています.特にCとC++では明らかです.ポインタを定義すると、ポインタが初期化されず、メモリ上の深刻な問題を引き起こす可能性があります.しかし、私たちも同じように重要な掃除を忘れがちです.Javaでは、ゴミ回収器が不要なオブジェクトが占有するメモリリソースの回収を担当します.
クリーンアップを実施しなければなりません
C++では、すべてのオブジェクトが破棄されるか、破棄されるべきです.C++がローカルオブジェクトを作成した場合、[右かっこ](Right Case)が境界であるオブジェクトの作用領域の最後に破棄アクションが発生します.オブジェクトがnewで作成されている場合は、C++のdeleteオペレータを呼び出し、対応する構造関数を呼び出します.deleteを呼び出すのを忘れた場合は、構造関数を呼び出すことはありません.メモリの漏洩が発生し、オブジェクトの他の部分もクリーンアップされません.
逆に、Javaではローカルオブジェクトの作成は許可されず、newを使用してオブジェクトを作成する必要があります.Javaにはオブジェクトを解放するdeleteもないので、ゴミ回収器がストレージスペースを解放してくれます.しかし,我々の学習が進むにつれて,ごみ回収は万能ではないことが分かった.すなわち,C++の構造関数に代わるごみ回収器とは考えられない.したがって,解放空間以外のクリーンアップ作業を行うには,適切なJavaメソッドを明確に呼び出さなければならない.
「ゴミ回収」でも「終了」でも、Java仮想マシンがメモリの消費に直面していない場合、ゴミ回収を実行してメモリを復元する時間を無駄にしないことを覚えておいてください.
ごみ回収器の動作方法
今から皆さんが興味を持っている話題を始めましょう.ゴミ回収器はどのように働いていますか.まず、私たちの以前の認識から言えば、スタック上でオブジェクトを作成するコストは非常に高いので、Javaはすべてのオブジェクトをスタック上で作成するコストも非常に高いと自然に考えています.しかし、ゴミ回収器はオブジェクトの作成速度を向上させるのに非常に効果的です.これは確かにJava仮想マシンの働き方なので、Javaがスタックから空間を割り当てる速度は、他の言語がスタックから空間を割り当てる速度に匹敵します.
Javaでは、スタックはコンベアのようなもので、新しいオブジェクトを割り当てるたびに1つ前に移動します.これは、オブジェクトストレージ空間の割り当て速度が非常に速いことを意味します.Javaの「スタックポインタ」は、単純に割り当てられていない領域に移動するだけで、C++スタック上の割り当て空間よりも効率的です.
実際、Javaのスタックはコンベアのようなものではありません.これは頻繁なメモリページスケジューリングを招き、ハードディスクから移動することになります.ページスケジューリングはパフォーマンスに著しく影響し、最終的には十分なオブジェクトを作成した後、メモリリソースが消費されます.秘密はごみ回収器で、ごみ回収器は仕事の时にメモリを回収しながら、スタックの中の対象をコンパクトに并べて、このように“スタックの指针”は简単にコンベアの始まりに近づくことができて、同じくできるだけメモリの间违いを避けました.これは高速で、無限の空間が割り当てられるスタックモデルです.
リファレンスカウントテクノロジ
Javaでのゴミ回収を先に理解するには、まずゴミ回収メカニズム:参照カウントを見てみましょう.簡単だが速度が遅いゴミ回収技術です.各オブジェクトにはカウンタがあり、参照がオブジェクトに接続されている場合、参照カウントに1を加算し、参照が役割ドメインから離れたりnullに設定されている場合、カウンタは1を減算します.管理コストは大きくありませんが、プログラム全体のライフサイクルを継続します.ごみ回収器は、すべてのオブジェクトを含むリストを巡回し、オブジェクトの参照数が0であることが判明すると、その占有スペースを解放します.この方法には、オブジェクトがループリファレンスを行うと、オブジェクトは回収されるはずですが、リファレンスカウントは0ではありません.実際、リファレンスカウントはゴミ回収の働き方を説明するのによく使われていますが、Java仮想マシンには適用されていないようです.
さらに、スタックまたは静的ストレージ領域に存在する参照に最終的に遡ることができる、任意の「アクティブ」オブジェクトについて.この参照チェーンは、複数のオブジェクト階層を通過する可能性があります.スタックと静的ストレージ領域からすべてのリファレンスを巡回すると、すべての生きているオブジェクトが見つかります.検出された各リファレンスについては、彼が参照したオブジェクトを追跡し、その後、このオブジェクトに含まれるすべてのリファレンスを繰り返して、スタックと静的ストレージ領域に根ざしたリファレンスが形成されたネットワークがすべてアクセスされるまで繰り返します.図の広さ優先検索に似ています.これにより、オブジェクトのインタラクティブな自己参照の現象が解決されます.
アダプティブごみ回収技術
この技術には、見つかったアクティブなオブジェクトを処理するための「停止-コピー」という方法がある.プログラムの実行を一時停止し、現在のスタックから別のスタックにすべての生存オブジェクトをコピーします.コピーされていないのはすべてゴミです.オブジェクトが新しいスタックにコピーされたとき、彼らはすぐ隣にいて、前述の方法で簡単に、新しい空間を直接割り当てることができます.
しかし、この方法は効率が低く、2つの原因があります.まず2つのスタックが必要で、それからこの分離された2つのスタックの間で往復して、実際には2倍の空間を維持しなければなりません.Java仮想機会の中には、必要に応じてスタックから大きなメモリをいくつか割り当て、レプリケーション動作はこれらの大きなメモリの間で発生します.2つ目はコピーです.プログラムが安定した状態になると、少量のゴミしか発生せず、ゴミも発生しない可能性がありますが、レプリケーション回収器はすべてのメモリを1つの場所から別の場所にコピーし、無駄になります.このような状況を避けるために、Java仮想マシンの中には、新しいゴミが発生しなければ、別の作業モードに変換されるというチェックがあります.このモードは「マーク-清掃」と呼ばれ、カウントリードによって生成されたオブジェクトの自己参照を解決する考え方とよく似ていますが、「生きている」オブジェクトを見つけてマークを与えます.このプロセスでは、任意のオブジェクトが回収されます.マークが完了したときにのみ、クリーンアップ動作が開始され、クリーンアップの過程で、マークされていないオブジェクトは解放され、レプリケーションは発生しません.そのため、メモリは不連続です.
ここで論じたJava仮想マシンでは,メモリ割り当てはいずれも大きな「ブロック」単位である.オブジェクトが大きい場合は、個別のブロックが使用されます.厳密には、「停止-コピー」では、古いオブジェクトを解放する前に、すべての生存オブジェクトを古いスタックから新しいスタックにコピーする必要があります.これにより、大量のメモリコピーが発生し、「ブロック」があれば、ゴミ回収器は回収時に廃棄されたブロックに直接オブジェクトをコピーすることができます.各ブロックは、生存しているかどうかを対応する代数で記録します.ブロックがどこかで参照されると、その代数が増加し、ゴミ回収器は、前回の回収動作後に新しく割り当てられたブロックを整理し、短命の一時オブジェクトを大量に処理するのに役立ちます.ごみ回収器は定期的に完全な清掃動作を行います.Javaの仮想的な機会を監視し、すべてのオブジェクトが安定し、ゴミ回収機の効率が低い場合は「タグ-清掃」に切り替え、最後にスタックスペースにまた多くの破片が発生すると「停止-複製」に切り替わる.これが「適応技術」です.
Java仮想マシンには、「インスタント」コンパイラテクノロジーや「不活性評価」など、速度を向上させるための追加テクノロジーがたくさんあります.後者の方法は一般的です.
しゅうちゅうしょり
Javaではfinalize()という方法があり、ゴミ回収器がオブジェクトが占有するストレージスペースを解放する準備ができたら、まずfinalizeメソッドを呼び出し、次のゴミ回収動作が発生すると、本当にオブジェクトが占有するメモリを回収すると仮定しています.だからfinalize方法を使うつもりなら、ゴミ回収の時に重要な掃除をすることができます.
なぜこの方法があるのか、Javaではゴミ回収されないオブジェクトがあるからです.ごみの回収はC++の中の分析構造に等しくない.したがって、オブジェクトを必要としない前に、いくつかのアクションを実行する必要があります.Javaは解析を提供していません.似たような仕事をするには、自分で手動でクリーンアップ作業を実行する一般的な方法を作成する必要があります.
実はゴミ回収器の運行時間は不確定で、JVMによって確定され、実行しても間欠的に実行されるが、JVMは通常メモリが不足していると感じた時にゴミ回収操作を行い、ゴミ回収を実行するためにもオーバーヘッドが必要であり、頻繁すぎると効率の低下を招き、プログラムの実行が終わるにつれて、ゴミ回収機が作成したオブジェクトのストレージスペースを解放していない場合、プログラムが終了するにつれて、リソースはすべてOSに返されます.
「ごみ回収」も「終結」も、必ず起こるとは限らない.Java仮想マシンがメモリの消費に直面していない場合は、ゴミ回収を実行してメモリを復元する時間を無駄にしません.
しゅうたんじょうけん
オブジェクトに適切にクリーンアップされていない部分がある限り,プログラムには暗い欠陥がある.finalize法は最終的にこの状況を発見するために使用できるが,finalize法は期待できない.以下に簡単な例を示します.
import static java.lang.System.out;

/** * Created by paranoid on 17-3-21. */
class Book{
    boolean checkedout = false;

    Book(boolean checkout){
        checkedout = checkout;
    }

    void checkIn(){
        checkedout = false;
    }

    //      
    protected void finalize(){
        if(checkedout){
            out.println("Error: checked out");
        }
    }
}

public class TerminationCondition {
    public static void main(String[] args){
        Book novel = new Book(true);
        novel.checkIn();

        new Book(true);

        System.gc();
    }
}

すべてのBookオブジェクトは、ごみとして回収される前にチェックインされるべきです.しかし、この例では1冊の本が署名されていない.finalize法が終端条件を検証しなければ,この欠陥の発見は困難である.
注意:システムがfinalizeメソッドを呼び出す時間は不確定ですが.gc()は強制回収であるが,システムにできるだけ早く処理するように通知するだけである.