やあ!

コンピューターのメモリ容量には限界があると言っても、あまり驚かないと思います :) ハード ドライブ (通常は RAM ストレージの何倍も大きい) でも、お気に入りのゲーム、テレビ番組、もっと。これを防ぐには、メモリの現在の状態を監視し、コンピュータから不要なファイルを削除する必要があります。Java プログラミングはこれらすべてとどのような関係があるのでしょうか? すべての!結局のところ、Java マシンはオブジェクトを作成するときに、そのオブジェクトにメモリを割り当てます。

実際の大規模なプログラムでは、数万、数十万のオブジェクトが作成され、それぞれのオブジェクトに独自のメモリが割り当てられます。しかし、これらすべてのオブジェクトはどれくらいの間存在すると思いますか? 彼らはプログラムの実行中ずっと「生きている」のでしょうか? もちろん違います。Java オブジェクトにはさまざまな利点がありますが、不滅ではありません :) オブジェクトには独自のライフサイクルがあります。今日はコードの作成から少し休憩して、このプロセスを見ていきます :) さらに、プログラムがどのように動作し、リソースがどのように管理されるかを理解するためにも非常に重要です。それでは、物の寿命はいつ始まるのでしょうか? 人間のように、その誕生、つまり創造から。

Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!

まず、Java 仮想マシンは、オブジェクトの作成に必要な量のメモリを割り当てます。次に、そのメモリへの参照を作成します。私たちの場合、その参照は という名前なcatので、それを追跡できます。次に、すべての変数が初期化され、コンストラクターが呼び出され、そして — た、だ! — 私たちの新しく鋳造されたオブジェクトは、独自の人生を生きています:)

オブジェクトの寿命はさまざまであるため、ここで正確な数字を提供することはできません。いずれの場合も、プログラム内でしばらく存続し、その機能を実行します。正確に言うと、オブジェクトへの参照がある限り、オブジェクトは「生きている」のです。参照がなくなるとすぐに、オブジェクトは「消滅」します。例:

public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}

Lamborghini Diablo 車オブジェクトは、メソッドの 2 行目で停止しますmain()。それへの参照は 1 つだけあり、その参照は に等しく設定されましたnull。Lamborghini Diablo への参照が残っていないため、割り当てられたメモリは「ゴミ」になります。これを行うために参照を null に設定する必要はありません。

public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}

ここでは 2 番目のオブジェクトを作成し、この新しいオブジェクトを参照に割り当てましたlamborghini。ここで、Lamborghini Gallardoオブジェクトには 2 つの参照がありますが、Lamborghini Diabloオブジェクトには 1 つもありません。これは、Diabloオブジェクトがゴミになったことを意味します。このとき、ガベージ コレクター (GC) と呼ばれる Java の組み込みメカニズムが機能します。

ガベージ コレクターは、メモリを解放する、つまりメモリから不要なオブジェクトを削除する役割を担う内部 Java メカニズムです。ここでロボット掃除機の写真を選んだのには理由があります。結局のところ、ガベージ コレクターはほぼ同じように動作します。つまり、バックグラウンドでプログラム内を「移動」し、ユーザー側でほとんど労力を費やすことなくガベージを収集します。その仕事は、プログラムで使用されなくなったオブジェクトを削除することです。

これにより、コンピュータ内のメモリが他のオブジェクトのために解放されます。レッスンの冒頭で、日常生活ではコンピューターの状態を監視し、不要なファイルを削除する必要があると述べたことを覚えていますか? Java オブジェクトの場合、ガベージ コレクターがこれを実行します。ガベージ コレクターはプログラムの実行中に繰り返し実行されます。技術的には可能ですが、明示的に呼び出したりコマンドを指定したりする必要はありません。後ほど、これについてさらに詳しく説明し、その動作をより詳細に分析します。

ガベージ コレクターは、オブジェクトに到達すると、オブジェクトを破棄する直前に、finalize()そのオブジェクトに対して特別なメソッドを呼び出します。このメソッドは、オブジェクトによって使用されている他のリソースを解放できます。メソッドはクラスfinalize()の一部ですObject。つまり、equals()前に説明したhashCode()およびメソッドに加えて、すべてのオブジェクトにこのメソッドがあるということです。toString()他の方法と異なるのは、何と言うか非常に気まぐれであるという点です。

特に、オブジェクトの破棄前に常に呼び出されるとは限りません。プログラミングは緻密な作業です。プログラマがコンピュータに何かをするように指示し、コンピュータがそれを実行します。あなたはすでにこの動作に慣れていると思いますので、最初は次の考えを受け入れるのが難しいかもしれません: 「オブジェクトが破棄される前に、クラスのメソッドが呼び出されます。あるいは、呼び出されないかもしれません。すべては状況によって異なりfinalize()ますObject」あなたの幸運を!」

それでも、それは本当です。Java マシン自体が、メソッドを呼び出すかどうかをfinalize()ケースバイケースで決定します。たとえば、実験として次のコードを実行してみましょう。

public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {
       for (int i = 0 ; i < 1000000; i++) {
           Cat cat = new Cat();
           cat = null; // This is when the first object becomes available to the garbage collector
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Cat object destroyed!");
   }
}

オブジェクトを作成しCat、次のコード行でその唯一の参照を null に設定します。そして私たちはそれを何百万回も繰り返します。このメソッドを明示的にオーバーライドしてfinalize()、コンソールに文字列を 100 万回 (オブジェクトを破棄するたびに 1 回Cat) 出力するようにしました。しかし、そうではありません。正確に言うと、私のコンピューターでは 37,346 回しか実行されませんでした。つまり、私のマシンにインストールされている Java マシンがメソッドの呼び出しを決定したのは 27 回に 1 回だけですfinalize()

他の場合には、ガベージ コレクションがそれなしで行われました。このコードを自分で実行してみてください。おそらく、異なる結果が得られるでしょう。ご覧のとおり、finalize()信頼できるパートナーとはほとんど言えません:) そこで、将来のためのちょっとしたアドバイス:finalize()重要なリソースを解放する方法に依存しないでください。JVM がそれを呼び出すかもしれませんし、呼び出さないかもしれません。知るか?

オブジェクトが生きている間に、オープンなデータベース接続など、パフォーマンスにとって非常に重要なリソースを保持している場合は、クラス内に特別なメソッドを作成してそれらを解放し、オブジェクトが存在しなくなったときにそれを明示的に呼び出すことをお勧めします。必要です。そうすれば、プログラムのパフォーマンスが低下しないことが確実にわかります。最初から、メモリを操作してゴミを削除することが非常に重要であると述べましたが、これは真実です。リソースを不適切に処理したり、不要なオブジェクトがクリーンアップされる方法を誤解すると、メモリ リークが発生する可能性があります。これは最もよく知られているプログラミングの間違いの 1 つです。

プログラマーがコードを誤って作成すると、新しく作成されるオブジェクトに毎回新しいメモリが割り当てられる一方、古い不要なオブジェクトはガベージ コレクターによる削除に利用できなくなる可能性があります。ロボット掃除機を例に挙げたので、ロボットを起動する前に、靴下を家の中に散らかし、ガラスの花瓶を割って、床一面にレゴの積み木を放置したらどうなるかを想像してみてください。もちろん、ロボットはその仕事をしようとしますが、ある時点で行き詰まってしまいます。

ロボット掃除機が適切に機能するには、床を良好な状態に保ち、ロボットが処理できないものはすべて取り除く必要があります。同じ原則が Java のガベージ コレクターにも当てはまります。クリーンアップできないオブジェクトがプログラム内に多数残っている場合 (靴下やロボット掃除機のレゴの積み木など)、ある時点でメモリ不足になります。また、フリーズするのはあなたのプログラムだけではなく、コンピュータ上で実行されている他のすべてのプログラムが影響を受ける可能性があります。彼らも十分なメモリを持っていない可能性があります。

これを覚える必要はありません。必要なのは、その仕組みの背後にある原理を理解することだけです。