こんにちは!今日のレッスンでは、Java のファントム参照について詳しく説明します。これらはどのようなリンクであり、なぜ「ファントム」と呼ばれるのか、またその使用方法は何ですか? 覚えているとおり、Java には 4 種類のリンクがあります。
-
StrongReference (オブジェクトの作成時に作成する通常の参照):
Cat cat = new Cat()
この例のcat は強参照です。
-
SoftReference (ソフトリファレンス)。これらのリンクについて講義を行いました。
-
WeakReference (弱い参照)。それらについての講義もここにありました。
-
PhantomReference (ファントムリファレンス)。
SoftReference
、WeakReference
およびPhantomReference
クラスから継承しますReference
。これらのクラスを操作する場合の最も重要なメソッドは次のとおりです。
-
get()
- このリンクが参照するオブジェクトを返します。 clear()
— オブジェクトへの参照を削除します。
SoftReference
これらのメソッドは、とに関する講義で覚えていますWeakReference
。リンクの種類が異なれば動作も異なることに留意することが重要です。今日は最初の 3 つのタイプについては詳しく説明しませんが、ファントム リンクについて説明します。他のタイプのリンクについても触れますが、ファントム リンクがそれらとどのように異なるかについて説明する部分のみです。行く!:) そもそもなぜファントムリンクが必要なのかから始めましょう。ご存知のとおり、ガベージ コレクター(ガベージ コレクターまたは gc) は、 不要な Java オブジェクトからメモリを解放する役割を果たします。コレクターは 2 つの「パス」でオブジェクトを削除します。 最初のパスではオブジェクトだけを調べ、必要に応じてそれらを「不要で削除する」としてマークします。このオブジェクトにオーバーライドされたメソッドがある場合finalize()
、そのメソッドが呼び出されます。あるいは、運次第では呼ばれません。おそらくこれが気まぐれなものであることを覚えているでしょうfinalize()
:) コレクターの 2 回目のパスでは、オブジェクトが削除され、メモリが解放されます。ガベージ コレクターのこの予期しない動作は、私たちに多くの問題を引き起こします。ガベージ コレクターがいつ動作し始めるのかは正確にはわかりません。メソッドが呼び出されるかどうかはわかりませんfinalize()
。さらに、操作中にfinalize()
オブジェクトへの強力なリンクが作成されるため、そのリンクはまったく削除されなくなります。メモリを大量に消費するシステムでは、これは簡単に を引き起こす可能性がありますOutOfMemoryError
。これらすべてが、私たちをファントム リンクの使用へと駆り立てるのです。重要なのは、これによりガベージ コレクターの動作が変更されるということです。オブジェクトへのファントム リンクのみが残っている場合、オブジェクトには次のものがあります。
-
メソッドが呼び出されます
finalize()
(オーバーライドされた場合)。 -
作業後に
finalize()
何も変更がなく、オブジェクトを削除できる場合は、オブジェクトへのファントム参照が特別なキューに置かれますReferenceQueue
。
clear()
。例を見てみましょう。まず、データを保存するテスト クラスを作成しましょう。
public class TestClass {
private StringBuffer data;
public TestClass() {
this.data = new StringBuffer();
for (long i = 0; i < 50000000; i++) {
this.data.append('x');
}
}
@Override
protected void finalize() {
System.out.println("У an object TestClass вызван метод finalize!!!");
}
}
より多くのメモリを使用するために、オブジェクトの作成時に意図的にデータを「ロード」します (各オブジェクトに 5,000 万個の「x」文字を追加します)。さらに、メソッドを具体的にオーバーライドして、finalize()
それが機能することを確認します。次に、 を継承するクラスが必要ですPhantomReference
。なぜそのようなクラスが必要なのでしょうか? それは簡単です。このようにして、メソッドに追加のロジックを追加してclear()
、ファントム参照が実際にクリアされたこと (したがって、オブジェクトが削除されたこと) を確認できます。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class MyPhantomReference<TestClass> extends PhantomReference<TestClass> {
public MyPhantomReference(TestClass obj, ReferenceQueue<TestClass> queue) {
super(obj, queue);
Thread thread = new QueueReadingThread<TestClass>(queue);
thread.start();
}
public void cleanup() {
System.out.println("Очистка фантомной ссылки! Удаление an object из памяти!");
clear();
}
}
次に、ガベージ コレクターが動作するのを待機する別のスレッドが必要になります。そうすると、ReferenceQueue
ファントム リンクがキューに表示されます。このようなリンクがキューに入るとすぐに、そのメソッドが呼び出されますcleanup()
。
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
public class QueueReadingThread<TestClass> extends Thread {
private ReferenceQueue<TestClass> referenceQueue;
public QueueReadingThread(ReferenceQueue<TestClass> referenceQueue) {
this.referenceQueue = referenceQueue;
}
@Override
public void run() {
System.out.println("Поток, отслеживающий очередь, стартовал!");
Reference ref = null;
//ждем, пока в очереди появятся ссылки
while ((ref = referenceQueue.poll()) == null) {
try {
Thread.sleep(50);
}
catch (InterruptedException e) {
throw new RuntimeException("Поток " + getName() + " был прерван!");
}
}
//How только в очереди появилась фантомная link - очистить ее
((MyPhantomReference) ref).cleanup();
}
}
そして最後に、メソッドが必要ですmain()
。それを別のクラスに置きましょうMain
。その中に、 object TestClass
、それへのファントム参照、およびファントム参照用のキューを作成します。その後、ガベージ コレクターを呼び出して、何が起こるか見てみましょう:)
import java.lang.ref.*;
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(10000);
ReferenceQueue<TestClass> queue = new ReferenceQueue<>();
Reference ref = new MyPhantomReference<>(new TestClass(), queue);
System.out.println("ref = " + ref);
Thread.sleep(5000);
System.out.println("Вызывается сборка мусора!");
System.gc();
Thread.sleep(300);
System.out.println("ref = " + ref);
Thread.sleep(5000);
System.out.println("Вызывается сборка мусора!");
System.gc();
}
}
コンソール出力: ref = MyPhantomReference@4554617c キューを監視するスレッドが開始されました。ガベージコレクションが呼ばれます!Finalize メソッドが TestClass オブジェクトで呼び出されました。ref = MyPhantomReference@4554617c ガベージ コレクションが呼び出されています。ファントムリンクをクリーンアップしています! メモリからオブジェクトを削除しています! ここで何が見えるでしょうか?すべては私たちの計画通りに起こりました!オブジェクト クラスにはメソッドがオーバーライドされておりfinalize()
、コレクターの実行中に呼び出されました。次に、ファントム リンクがキューに登録されましたReferenceQueue
。clear()
そこには、 (cleanup()
コンソールに出力を追加するために実行した)というメソッドがありました。その結果、オブジェクトはメモリから削除されました。これで、それがどのように機能するかを正確に理解できました :) もちろん、ファントム リンクに関連する理論をすべて暗記する必要はありません。ただし、少なくとも要点だけは覚えておけば大丈夫です。 まず、これらはすべての中で最も弱いリンクです。これらは、オブジェクトへの参照が他にない場合にのみ機能します。上記のリンクのリストは強度の降順です: StrongReference
-> SoftReference
-> WeakReference
->PhantomReference
ファントム リンクは、オブジェクトへの強いリンク、柔らかいリンク、または弱いリンクがない場合にのみ戦闘に入ります:) 次に、メソッドget()
ファントムリンクの場合は常に を返しますnull
。以下は、3 つの異なるタイプの車に対して 3 つの異なるタイプのリンクを作成する簡単な例です。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
HybridAuto hybrid = new HybridAuto();
F1Car f1car = new F1Car();
SoftReference<Sedan> softReference = new SoftReference<>(sedan);
System.out.println(softReference.get());
WeakReference<HybridAuto> weakReference = new WeakReference<>(hybrid);
System.out.println(weakReference.get());
ReferenceQueue<F1Car> referenceQueue = new ReferenceQueue<>();
PhantomReference<F1Car> phantomReference = new PhantomReference<>(f1car, referenceQueue);
System.out.println(phantomReference.get());
}
}
コンソール出力: Sedan@4554617c HybridAuto@74a14482 null このメソッドは、get()
ソフト リンクとウィーク リンクについてはまったく通常のオブジェクトを返しましたが、null
ファントム リンクについてはまったく正常なオブジェクトを返しました。 第三に、ファントム参照の使用の主な領域は、メモリからオブジェクトを削除するための複雑な手順です。それだけです!:) これで今日のレッスンは終わりです。しかし、理論だけでは先に進むことはできません。問題の解決に戻りましょう。:)
GO TO FULL VERSION