프로젝트에서 PhantomReference를 사용한 적이 있습니까?


89

내가 아는 유일한 것은 약 PhantomReference이다

  • get()메서드 를 사용 하면 항상 null개체가 아닌 반환됩니다 . 그것의 용도는 무엇입니까?
  • 를 사용하여 메서드 PhantomReference에서 개체를 부활시킬 수 없음을 확인합니다 finalize.

그러나이 개념 / 클래스의 용도는 무엇입니까?

프로젝트에서 이것을 사용한 적이 있습니까? 아니면 우리가 이것을 사용해야 할 예가 있습니까?



PhantomReference의 참조 된 obj를 가져올 수 없기 때문에 완전한 잘못된 이름입니다. FakeReference또는 NonReference.
Pacerier

다음은 코드가있는 다른 스레드입니다. stackoverflow.com/q/43311825/632951
Pacerier

답변:


47

객체 생성 및 파괴를 모니터링하기 위해 단순하고 매우 특수한 종류의 메모리 프로파일 러PhantomReference 에서 s를 사용했습니다 . 파괴를 추적하기 위해 그것들이 필요했습니다. 그러나 접근 방식은 오래되었습니다. (J2SE 1.4를 대상으로 2004 년에 작성되었습니다.) 전문 프로파일 링 도구는 훨씬 더 강력하고 안정적이며 JMX 또는 에이전트 및 JVMTI와 같은 최신 Java 5 기능도 사용할 수 있습니다.

PhantomReferences (항상 참조 대기열과 함께 사용됨) finalize는 몇 가지 문제 가있는 것보다 우수 하므로 피해야합니다. 주로 개체에 다시 도달 할 수 있도록합니다. 이것은 파이널 라이저 가디언 관용구 (-> 'Effective Java'에서 더 읽어보기)로 피할 수 있습니다. 그래서 그들은 또한 새로운 finalize 입니다.

또한 PhantomReferences

객체가 메모리에서 제거 된시기를 정확히 확인할 수 있습니다. 사실 그들은 그것을 결정하는 유일한 방법입니다. 이것은 일반적으로 그다지 유용하지 않지만 큰 이미지 조작과 같은 특정 상황에서 유용 할 수 있습니다. 이미지가 가비지 수집되어야 함을 확실히 알고 있다면 다음 이미지를로드하기 전에 실제로 될 때까지 기다릴 수 있습니다. , 따라서 두려운 OutOfMemoryError의 가능성을 줄입니다. ( enicholas 에서 인용 .)

그리고 psd 가 먼저 썼 듯이 Roedy Green은 참고 문헌에 대한 좋은 요약을 가지고 있습니다.


21

Java Glossary 의 일반적인 요약 표 설명 입니다.

와 코스 일치의 어떤 PhantomReference 문서 :

수집기가 참조 대상을 회수 할 수 있다고 결정한 후 대기열에 추가되는 팬텀 참조 개체입니다. Phantom 참조는 Java 종료 메커니즘에서 가능한 것보다 더 유연한 방식으로 사전 정리 조치를 스케줄링하는 데 가장 자주 사용됩니다.

그리고 마지막으로, 모든 세부 사항 ( 좋은 읽기 ) : Java Reference Objects (또는 How I Learned to Stop Worrying and Love OutOfMemoryError) .

즐거운 코딩입니다. (그러나 질문에 답하기 위해 WeakReferences 만 사용했습니다.)


Btw, 그 기사에 대한 질문. PhantomReference 섹션에서 그는이 두 테이블을 통해 연결 개체에 대한 강력한 참조를 유지합니다. 이는 연결에 도달 할 수 없음을 의미합니다 (풀 인스턴스 자체에 도달 할 수 없다고 가정). 따라서 해당 PhantomReferences는 대기열에 포함되지 않습니다. 아니면 내가 뭔가를 놓치고 있습니까?
shrini1000

1
와, kdgregory의 기사는 +10
Pacerier의

14

팬텀 레퍼런스 사용법에 대한 훌륭한 설명 :

팬텀 참조는 객체가 메모리에서 제거되었음을 알 수있는 안전한 방법입니다. 예를 들어, 큰 이미지를 처리하는 애플리케이션을 고려하십시오. 큰 이미지가 이미 가비지 수집 준비가 된 메모리에있을 때 큰 이미지를 메모리에로드한다고 가정합니다. 이 경우 새 이미지를로드하기 전에 이전 이미지가 수집 될 때까지 기다립니다. 여기에서 가상 참조는 유연하고 안전하게 선택할 수있는 옵션입니다. 이전 이미지 객체가 완료되면 이전 이미지의 참조가 ReferenceQueue에 추가됩니다. 해당 참조를받은 후 새 이미지를 메모리에로드 할 수 있습니다.


12

나는의 실용적이고 유용한 사용 사례 발견 PhantomReference되는 org.apache.commons.io.FileCleaningTracker공유지-IO 프로젝트를. FileCleaningTracker마커 개체가 가비지 수집되면 실제 파일을 삭제합니다.
주목할 점은 Tracker클래스를 확장하는 PhantomReference클래스입니다.


5

이것은 JAVA 9에서 더 이상 사용되지 않아야합니다! 대신
사용하십시오 java.util.Cleaner! (또는 sun.misc.Cleaner이전 JRE에서)

원본 게시물 :


나는 PhantomReferences의 사용이 종료 자 메서드와 거의 같은 양의 함정을 가지고 있음을 발견했습니다 (하지만 제대로 된 후에는 문제가 적습니다). Java 8 용 작은 솔루션 (PhantomReferences를 사용하기위한 매우 작은 프레임 워크)을 작성했습니다. 객체가 제거 된 후 실행할 콜백으로 람다 식을 사용할 수 있습니다. 닫아야하는 내부 리소스에 대한 콜백을 등록 할 수 있습니다. 이것으로 훨씬 더 실용적이기 때문에 나를 위해 작동하는 솔루션을 찾았습니다.

https://github.com/claudemartin/java-cleanup

다음은 콜백 등록 방법을 보여주는 간단한 예입니다.

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

그리고 위와 거의 동일한 자동 닫기 방법이 있습니다.

this.registerAutoClose(this.resource);

질문에 답하려면 :

[그런 다음 그것의 사용은 무엇입니까]

존재하지 않는 것을 정리할 수 없습니다. 그러나 여전히 존재하고 제거 할 수 있도록 정리해야하는 리소스가있을 수 있습니다.

그러나이 개념 / 클래스의 용도는 무엇입니까?

디버깅 / 로깅 이외의 다른 효과로 반드시 수행 할 필요는 없습니다. 아니면 통계를 위해. GC의 알림 서비스처럼 보입니다. 개체가 제거되면 관련성이없는 집계 데이터를 제거하는 데 사용할 수도 있습니다 (하지만 더 나은 솔루션이있을 수 있음). 예제에서는 종종 데이터베이스 연결을 닫아야한다고 언급하지만 트랜잭션으로 작업 할 수 없기 때문에 이것이 얼마나 좋은 생각인지 모르겠습니다. 애플리케이션 프레임 워크는이를위한 훨씬 더 나은 솔루션을 제공합니다.

당신의 프로젝트에서 이것을 사용한 적이 있습니까, 아니면 우리가 이것을 사용해야 할 예가 있습니까? 아니면이 개념이 인터뷰 관점에서 만들어진 것입니까?)

주로 로깅에만 사용합니다. 따라서 제거 된 요소를 추적하고 GC가 어떻게 작동하고 조정할 수 있는지 확인할 수 있습니다. 나는 이런 식으로 중요한 코드를 실행하지 않을 것입니다. 무언가를 닫아야하는 경우 try-with-resource-statement에서 수행해야합니다. 그리고 메모리 누수가 없는지 확인하기 위해 단위 테스트에서 사용합니다. jontejj와 같은 방식으로합니다. 하지만 내 솔루션은 좀 더 일반적입니다.


예, 내 솔루션 "java-cleanup"처럼. 둘 다 추상화이므로 직접 처리 할 필요가 없습니다.
Claude Martin

3

단위 테스트에서 PhantomReference를 사용하여 테스트중인 코드가 일부 개체에 대한 비 필수 참조를 유지하지 않았는지 확인했습니다. ( 원래 코드 )

import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import com.google.common.testing.GcFinalization;

/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}

/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;

private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}

/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}

/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}

그리고 테스트 :

@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
    Object holdMeTight = new String("Hold-me-tight");
    FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
    try
    {
    finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
    fail("holdMeTight was held but memory leak tester did not discover it");
    }
    catch(AssertionError expected)
    {
    assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
    }
}

2

더 적절한 WeakReference곳에 사용 하는 것이 일반적 PhantomReference입니다. 이렇게하면 WeakReference가비지 수집기에서 a 를 지우거나 대기열에 넣은 후 개체를 부활시킬 수있는 특정 문제를 방지 할 수 있습니다. 보통 사람들이 어리석은 버거를하지 않기 때문에 그 차이는 중요하지 않습니다.

방법이 작동 PhantomReference하는 척할 수 없기 때문에 사용 은 좀 더 방해가되는 경향이 있습니다 get. 예를 들어 Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference>는 실제로 IdentityHashMap에 적합한 위치 중 하나입니다. 주의 강한 참조가 PhantomReference로 유지, 지시 대상을하지만 .

실제로 약한 참조의 경우 finalize가 obj를 다시 만들 수 있음을 의미합니까? weakref.get를 반환 할 수 null있고 나중에 obj를 반환 할 수 있습니까?
Pacerier

@Pacerier finalize는 객체를 다시 생성하지 않습니다. 에서 WeakReference반환 null되어 get대기열에 추가 된 후 객체에 다시 강력하게 도달 할 수 있습니다. / (user166390 : 참조 대상에 키가 지정된 맵에서와 마찬가지로 참조 WeakHashMap의 식별 맵이 아닙니다.)
Tom Hawtin-tackline

1

get () 메서드를 사용하면 항상 객체가 아닌 null을 반환합니다. [그런 다음 그것의 사용은 무엇입니까]

호출하는 데 유용한 메서드 (보다는 get())는 isEnqueued()또는 referenceQueue.remove()입니다. 이러한 메서드를 호출하여 개체의 가비지 수집의 마지막 라운드에서 발생해야하는 작업을 수행합니다.

처음에는 객체에 finalize()메서드가 호출되어 닫는 후크도 넣을 수 있습니다. 그러나 다른 사람들이 말했듯이 정리를 수행하는 더 확실한 방법이나 가비지 수집 전후 또는 더 일반적으로 개체 수명이 끝날 때 수행해야하는 작업이있을 수 있습니다.


1

Jetty의 LeakDetector 클래스 PhantomReferences에서의 또 다른 실용적인 사용을 발견했습니다 .

Jetty는 LeakDetector클래스를 사용 하여 클라이언트 코드가 리소스를 얻었는지 여부를 감지하지만 리소스를 해제하지 않으며 LeakDetector클래스는 PhantomReferences이 목적을 위해를 사용합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.