JavaRush /Java Blog /Random-TL /PhantomReference sa Java

PhantomReference sa Java

Nai-publish sa grupo
Kamusta! Sa aralin ngayon, tatalakayin natin nang detalyado ang tungkol sa Phantom References sa Java. Anong uri ng mga link ang mga ito, bakit sila tinatawag na "phantom" at kung paano gamitin ang mga ito? Tulad ng naaalala mo, mayroong 4 na uri ng mga link sa Java:
  1. StrongReference (mga regular na sanggunian na ginagawa namin kapag lumilikha ng isang bagay):

    Cat cat = new Cat()

    pusa sa halimbawang ito ay isang Malakas na sanggunian.

  2. SoftReference (malambot na sanggunian). Nagkaroon kami ng lecture tungkol sa mga link na ito.

  3. WeakReference (mahinang sanggunian). Nagkaroon din ng lecture tungkol sa kanila, dito .

  4. PhantomReference (phantom reference).

Ang mga bagay ng huling tatlong uri ay nai-type (halimbawa, SoftReference<Integer> , WeakReference<MyClass> ). Mga klase SoftReference, WeakReferenceat PhantomReferencemagmana mula sa klase Reference. Ang pinakamahalagang paraan kapag nagtatrabaho sa mga klase na ito ay:
  • get()- ibinabalik ang bagay na tinutukoy ng link na ito;

  • clear()— tinatanggal ang isang sanggunian sa isang bagay.

Naaalala mo ang mga pamamaraang ito mula sa mga lektura tungkol sa SoftReferenceat WeakReference. Mahalagang tandaan na ang mga ito ay gumagana sa iba't ibang uri ng mga link. Ngayon hindi namin isasaalang-alang ang unang tatlong uri nang detalyado, ngunit tatalakayin ang tungkol sa mga link ng phantom. Tatalakayin din natin ang iba pang mga uri ng mga link, ngunit sa bahagi lamang kung saan pag-uusapan natin kung paano naiiba ang mga phantom link sa kanila. Go! :) Magsimula tayo sa kung bakit kailangan natin ng mga phantom link sa unang lugar. Tulad ng alam mo, ang kolektor ng basura (Garbage Collector o gc) ay may pananagutan sa pagpapalaya ng memorya mula sa mga hindi kinakailangang Java object . Tinatanggal ng kolektor ang bagay sa dalawang "pass". Sa unang pass, tumitingin lamang ito sa mga bagay, at, kung kinakailangan, minarkahan ang mga ito bilang "hindi kailangan at dapat tanggalin." Kung ang bagay na ito ay may na-override na pamamaraan finalize(), ito ay tinatawag. O hindi ito tinatawag - depende sa iyong suwerte. Malamang naaalala mo na ito finalize()ay isang pabagu-bagong bagay :) Sa pangalawang pass ng kolektor, ang bagay ay tinanggal at ang memorya ay napalaya. Ang hindi mahuhulaan na pag-uugali na ito ng kolektor ng basura ay lumilikha ng ilang mga problema para sa atin. Hindi natin alam kung kailan eksaktong magsisimulang magtrabaho ang tagakolekta ng basura. Hindi namin alam kung ang pamamaraan ay tatawagin finalize(). Dagdag pa, sa panahon ng operasyon finalize()ang isang malakas na link sa bagay ay maaaring malikha, at pagkatapos ay hindi ito tatanggalin sa lahat. Sa memory-hungry system, madali itong humantong sa OutOfMemoryError. Ang lahat ng ito ay nagtutulak sa amin patungo sa paggamit ng mga phantom link . Ang punto ay nababago nito ang pag-uugali ng kolektor ng basura. Kung mayroon lamang mga phantom link na natitira sa object, mayroon itong:
  • ang pamamaraan ay tinatawag finalize()(kung ito ay na-override);

  • kung pagkatapos ng trabaho finalize()ay walang nagbago at ang bagay ay maaari pa ring tanggalin, isang phantom reference sa bagay ay inilalagay sa isang espesyal na pila - ReferenceQueue.

Самое важное, что нужно понимать при работе с фантомными linkми, — an object не удаляется из памяти до тех пор, пока его фантомная link находится в этой очереди. Он будет удален только после того, How у фантомной ссылки будет вызван метод clear(). Давай рассмотрим пример. Для начала создадим тестовый класс, который будет хранить в себе Howие-то данные.
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!!!");
   }
}
Мы специально How следует «загружаем» an objectы данными при создании (добавляем в каждый an object по 50 миллионов символов «х»), чтобы занять побольше памяти. Кроме того, мы специально переопределяем метод finalize(), чтобы увидеть, что он сработал. Далее нам понадобится класс, который будет наследоваться от PhantomReference. Зачем нам нужен такой класс? Все просто. Так мы сможем добавить дополнительную логику к методу clear(), чтобы увидеть, что очистка фантомной ссылки действительно произошла (а значит, an object удален).
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 появятся фантомные ссылки. Как только такая link попадет в очередь, у нее будет вызван метод 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. В нем мы создадим an 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 Поток, отслеживающий очередь, стартовал! Вызывается сборка мусора! У an object TestClass вызван метод finalize!!! ref = MyPhantomReference@4554617c Вызывается сборка мусора! Очистка фантомной ссылки! Удаление an object из памяти! What же мы здесь видим? Все произошло, How мы и планировали! У нашего класса an object был переопределен метод finalize(), и он был вызван во время работы сборщика. Далее, фантомная link была помещена в очередь ReferenceQueue. Там у нее был вызван метод clear() (из которого мы сделали cleanup(), чтобы добавить вывод в консоль). В итоге an object был удален из памяти. Теперь ты видишь, How именно это работает :) Конечно, тебе не нужно зазубривать наизусть всю связанную с фантомными linkми теорию. Но будет хорошо, если ты будешь помнить хотя бы главные моменты. Во-первых, это самые слабые ссылки из всех. Они вступают в работу только когда на an object не осталось ниHowих других ссылок. Список ссылок, которые мы привели выше, идет по «убыванию силы»: StrongReference -> SoftReference -> WeakReference -> PhantomReference Фантомная link вступит в бой только когда на наш an object не будет ни Strong, ни Soft, ни Weak ссылок :) Во-вторых, метод get() для фантомной ссылки всегда возвращает null. Вот простой пример, где мы создаем три разных типа ссылок для трех разных видов автомобилей:
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() вернул вполне нормальные an objectы для мягкой ссылки и слабой ссылки, но вернул null для фантомной. В-третьих, основная область использование фантомных ссылок -— сложные proceduresы удаления an objectов из памяти. Вот и все! :) На этом наше сегодняшнее занятие окончено. Но на одной теории далеко не уедешь, поэтому пора возвращаться к решению задач! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION