CodeGym /Java Blogu /Rastgele /Java'da Fantom Referanslar
John Squirrels
Seviye
San Francisco

Java'da Fantom Referanslar

grupta yayınlandı
MERHABA! Bugünkü tartışmamızda, Java'daki "hayalet referanslar" (PhantomReference) hakkında detaylı olarak konuşacağız. Bunlar ne tür referanslar? Neden "hayali referanslar" olarak adlandırılıyorlar? Nasıl kullanılırlar? Hatırlayacağınız gibi, Java'nın 4 tür referansı vardır:
  1. StrongReference (nesne oluştururken oluşturduğumuz sıradan referanslar):

    Cat cat = new Cat()

    Bu örnekte, kedi güçlü bir referanstır.

  2. SoftReference (yumuşak referans). Bu tür referanslarla ilgili bir dersimiz vardı .

  3. WeakReference (zayıf referans). Burada onlarla ilgili bir ders de vardı .

  4. PhantomReference (hayali referans).

Son üçü, tür parametrelerine sahip genel türlerdir (örneğin, SoftReference<Integer> , WeakReference<MyClass> ). SoftReference , WeakReference ve PhantomReference sınıfları , Reference sınıfından devralınır . Bu sınıflarla çalışırken en önemli yöntemler şunlardır:
  • get() — başvurulan nesneyi döndürür;

  • clear() — nesneye yapılan başvuruyu kaldırır.

Bu yöntemleri SoftReference ve WeakReference derslerinden hatırlarsınız . Farklı referans türleri ile farklı şekilde çalıştıklarını hatırlamak önemlidir. Bugün ilk üç türe girmeyeceğiz. Bunun yerine hayalet referanslardan bahsedeceğiz. Diğer referans türlerine de değineceğiz, ancak sadece onların hayali referanslardan ne kadar farklı olduklarına değineceğiz. Hadi gidelim! :) Öncelikle, neden hayali referanslara ihtiyacımız var? Bildiğiniz gibi çöp toplayıcı (gc), gereksiz Java nesneleri tarafından kullanılan belleği serbest bırakır. Toplayıcı, bir nesneyi iki "geçişte" siler. İlk geçişte, yalnızca nesnelere bakar ve gerekirse onları "gereksiz" ("silinecek" anlamına gelir) olarak işaretler. Eğerfinalize()yöntem nesne için geçersiz kılındı, çağrılır. Ya da belki çağrılmadı - hepsi sadece şanslı olup olmadığına bağlı. Muhtemelen bunun kararsız olduğunu hatırlıyorsunuzdur finalize():) Çöp toplayıcının ikinci geçişinde nesne silinir ve hafıza boşaltılır. Çöp toplayıcının öngörülemeyen davranışı bizim için bir dizi sorun yaratır. Çöp toplayıcının ne zaman çalışmaya başlayacağını tam olarak bilmiyoruz. Yöntemin çağrılıp çağrılmayacağı bilinmiyor finalize(). Ayrıca, yöntemi yürütülürken bir nesneye güçlü bir referans oluşturulabilir finalize(), bu durumda nesne hiçbir şekilde silinmeyecektir. Kullanılabilir bellek üzerinde ağır taleplerde bulunan programlar için bu, kolayca bir OutOfMemoryError. Bütün bunlar bizi hayali referanslar kullanmaya itiyor.. Gerçek şu ki, bu çöp toplayıcının davranışını değiştiriyor. Nesnenin yalnızca hayali referansları varsa, o zaman:
  • finalize() yöntemi çağrılır (geçersiz kılınırsa)

  • finalize() yöntemi bittiğinde hiçbir şey değişmezse ve nesne hâlâ silinebiliyorsa, o zaman nesneye yönelik hayali başvuru özel bir kuyruğa yerleştirilir: ReferenceQueue .

Hayali referanslarla çalışırken anlaşılması gereken en önemli şey, hayali referansı bu kuyruğa girene kadar nesnenin bellekten silinmemesidir. Yalnızca hayali referansta clear() yöntemi çağrıldıktan sonra silinecektir . Bir örneğe bakalım. İlk olarak, bir tür veri depolayacak bir test sınıfı oluşturacağız.

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("The finalize method has been called on the TestClass object");
   }
}
Nesneler oluşturduğumuzda, bellekte daha fazla yer kaplamaları için kasıtlı olarak onlara ağır bir "yük" (her nesneye 50 milyon "x" karakteri ekleyerek) vereceğiz. Ayrıca, çalıştığını görmek için finalize() yöntemini geçersiz kılıyoruz. Ardından, PhantomReference öğesinden miras alacak bir sınıfa ihtiyacımız var . Neden böyle bir sınıfa ihtiyacımız var? Her şey basit. Bu , hayali referansın gerçekten temizlendiğini (bu, nesnenin silindiği anlamına gelir) doğrulamak için clear() yöntemine ek mantık eklememize izin verecektir .

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("Cleaning up a phantom reference! Removing an object from memory!");
       clear();
   }
}
Ardından, çöp toplayıcının işini yapmasını bekleyecek ayrı bir iş parçacığına ihtiyacımız var ve referans kuyruğumuzda hayali bağlantılar görünecek . Böyle bir başvuru kuyruğa girer girmez, üzerinde cleanup() yöntemi çağrılır:

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("The thread monitoring the queue has started!");
       Reference ref = null;

       // Wait until the references appear in the queue
       while ((ref = referenceQueue.poll()) == null) {

           try {
               Thread.sleep(50);
           }

           catch (InterruptedException e) {
               throw new RuntimeException("Thread " + getName() + " was interrupted!");
           }
       }

       // As soon as a phantom reference appears in the queue, clean it up
       ((MyPhantomReference) ref).cleanup();
   }
}
Son olarak, ayrı bir Main sınıfına koyacağımız main() yöntemine ihtiyacımız var . Bu yöntemde, bir TestClass nesnesi, ona bir hayalet referans ve hayali referanslar için bir kuyruk oluşturacağız . Ondan sonra çöp toplayıcıyı arayalım bakalım ne olacak :)

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("Collecting garbage!");

       System.gc();
       Thread.sleep(300);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Collecting garbage!");

       System.gc();
   }
}
Konsol çıktısı:

ref = MyPhantomReference@4554617c 
The thread monitoring the queue has started! 
Collecting garbage! 
The finalize method has been called on the TestClass object 
ref = MyPhantomReference@4554617c 
Collecting garbage! 
Cleaning up a phantom reference! 
Removing an object from memory! 
Burada ne görüyoruz? Her şey planladığımız gibi oldu! Nesnemizin finalize() yöntemi geçersiz kılındı ​​ve çöp toplayıcı çalışırken çağrıldı. Daha sonra, hayali referans ReferenceQueue içine yerleştirildi . Oradayken clear() yöntemi çağrıldı ( konsola çıktı almak için cleanup() adını verdiğimiz ). Son olarak, nesne bellekten silindi. Şimdi bunun nasıl çalıştığını tam olarak görüyorsunuz :) Tabii ki hayalet referanslarla ilgili tüm teorileri ezberlemenize gerek yok. Ama en azından ana noktaları hatırlasan iyi olur. Birincisi, bunlar en zayıf referanslardır. Yalnızca nesneye başka hiçbir referans kalmadığında devreye girerler. Yukarıda verdiğimiz referansların listesi, en güçlüden en zayıfa doğru azalan sırada sıralanmıştır: StrongReference -> SoftReference -> WeakReference -> PhantomReference Bir hayali referans, yalnızca nesnemize güçlü, yumuşak veya zayıf referanslar olmadığında savaşa girer: ) İkincisi, get() yöntemi hayali bir başvuru için her zaman null değerini döndürür . Üç farklı araba türü için üç farklı türde referans oluşturduğumuz basit bir örnek:

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());

   }
}
Konsol çıktısı:

Sedan@4554617c
HybridAuto@74a14482 
null
get () yöntemi, yumuşak ve zayıf başvurular için tamamen sıradan nesneler döndürdü, ancak hayali başvuru için boş döndürdü . Üçüncüsü, hayali referanslar, nesneleri bellekten silmek için karmaşık prosedürlerde kullanılır. Bu kadar! :) Bu, bugünkü dersimizi sonlandırıyor. Ancak teoride tek başına uzağa gidemezsiniz, bu yüzden görev çözmeye geri dönme zamanı! :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION