CodeGym/Java Blog/무작위의/Java의 팬텀 참조
John Squirrels
레벨 41
San Francisco

Java의 팬텀 참조

무작위의 그룹에 게시되었습니다
회원
안녕! 오늘 토론에서는 Java의 "팬텀 참조"(PhantomReference)에 대해 자세히 이야기하겠습니다. 이들은 어떤 종류의 참조입니까? "유령 참조"라고 하는 이유는 무엇입니까? 그들은 어떻게 사용됩니까? 기억하시겠지만 Java에는 4가지 종류의 참조가 있습니다.
  1. StrongReference (객체를 생성할 때 생성하는 일반 참조):

    Cat cat = new Cat()

    이 예에서 cat은 강력한 참조입니다.

  2. SoftReference (소프트 참조). 우리는 그러한 참조에 대한 교훈을 얻었습니다.

  3. WeakReference (약한 참조). 여기에 대한 교훈도 있었습니다 .

  4. PhantomReference (팬텀 참조).

마지막 3개는 유형 매개변수가 있는 제네릭 유형입니다(예: SoftReference<Integer> , WeakReference<MyClass> ). SoftReference , WeakReferencePhantomReference 클래스 Reference 클래스 에서 상속됩니다 . 이러한 클래스로 작업할 때 가장 중요한 방법은 다음과 같습니다.
  • get() — 참조된 객체를 반환합니다.

  • clear() — 개체에 대한 참조를 제거합니다.

SoftReferenceWeakReference 에 대한 단원에서 이러한 메서드를 기억합니다 . 서로 다른 종류의 참조와 다르게 작동한다는 점을 기억하는 것이 중요합니다. 오늘 우리는 처음 세 가지 유형에 대해서는 다루지 않을 것입니다. 대신 팬텀 참조에 대해 이야기하겠습니다. 우리는 다른 유형의 참조에 대해 다룰 것이지만 팬텀 참조와 어떻게 다른지에 대해서만 설명합니다. 갑시다! :) 먼저 팬텀 참조가 필요한 이유는 무엇입니까? 아시다시피 가비지 수집기(gc)는 불필요한 Java 객체가 사용하는 메모리를 해제합니다. 컬렉터는 두 개의 "패스"에서 개체를 삭제합니다. 첫 번째 단계에서는 개체만 보고 필요한 경우 개체를 "불필요"("삭제할" 의미)로 표시합니다. 만약finalize()메서드가 개체에 대해 재정의된 경우 호출됩니다. 또는 호출되지 않았을 수도 있습니다. 모두 운이 좋은지 여부에 달려 있습니다. 변덕스럽다는 것을 기억하실 것 finalize()입니다 :) 가비지 수집기의 두 번째 패스에서 개체가 삭제되고 메모리가 해제됩니다. 가비지 수집기의 예측할 수 없는 동작은 많은 문제를 야기합니다. 가비지 컬렉터가 언제 실행되기 시작할지는 정확히 알 수 없습니다. 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("The finalize method has been called on the TestClass object");
   }
}
개체를 만들 때 더 많은 메모리를 차지하기 위해 의도적으로 막대한 "부하"(각 개체에 5천만 개의 "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("Cleaning up a phantom reference! Removing an object from memory!");
       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("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();
   }
}
마지막으로 main() 메서드가 필요하며 이를 별도의 Main 클래스 에 넣습니다 . 이 메서드에서 우리는 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("Collecting garbage!");

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

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

       Thread.sleep(5000);

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

       System.gc();
   }
}
콘솔 출력:
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!
여기서 무엇을 볼 수 있습니까? 우리가 계획한대로 모든 일이 일어났습니다! 객체의 finalize() 메서드가 재정의되었으며 가비지 수집기가 실행되는 동안 호출되었습니다. 다음으로 팬텀 참조를 ReferenceQueue에 넣었 습니다 . 거기에 있는 동안 clear() 메서드가 호출되었습니다(우리는 콘솔에 출력하기 위해 cleanup()을 호출했습니다). 마지막으로 개체가 메모리에서 삭제되었습니다. 이제 이것이 어떻게 작동하는지 정확히 알 수 있습니다 :) 물론 팬텀 참조에 대한 모든 이론을 암기할 필요는 없습니다. 하지만 적어도 요점만 기억한다면 좋을 것이다. 첫째, 이들은 가장 약한 참조입니다. 개체에 대한 다른 참조가 남아 있지 않은 경우에만 작동합니다. 위에서 제공한 참조 목록은 가장 강한 것부터 가장 약한 것까지 내림차순으로 정렬됩니다. StrongReference -> SoftReference -> WeakReference -> PhantomReference 팬텀 참조는 객체에 대한 강하거나 약하거나 약한 참조가 없을 때만 전투에 들어갑니다. ) 둘째, 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 () 메서드는 소프트 참조와 약한 참조에 대해 완전히 일반 개체를 반환했지만 팬텀 참조에 대해서는 null을 반환했습니다. 셋째, 팬텀 참조는 주로 메모리에서 개체를 삭제하는 복잡한 절차에 사용됩니다. 그게 다야! :) 이것으로 오늘 수업을 마칩니다. 그러나 이론만으로는 멀리 갈 수 없으므로 문제 해결로 돌아갈 때입니다! :)
코멘트
  • 인기
  • 신규
  • 이전
코멘트를 남기려면 로그인 해야 합니다
이 페이지에는 아직 코멘트가 없습니다