문제

팬텀 참조는 사후 작업에 사용됩니다.Java 사양에 따르면 팬텀 참조 객체 팬텀 참조 자체가 정리될 때까지 할당이 취소되지 않습니다.

내 질문은 다음과 같습니다이 기능(객체가 할당 취소되지 않음)은 어떤 목적으로 사용됩니까?

(내가 생각해낸 유일한 아이디어는 네이티브 코드가 객체에 대해 사후 정리를 수행하도록 허용하는 것이지만 별로 설득력이 없습니다.)

도움이 되었습니까?

해결책

제가 생각할 수 있는 할당 해제를 방지할 수 있는 유일한 좋은 사용 사례는 JNI로 구현된 비동기 데이터 소스가 참조된 개체에 쓰는 경우이며, 개체에 쓰기를 중지하려면 중단하라는 지시를 받아야 하는 경우입니다. 메모리가 재활용되기 전에.이전 할당 해제가 허용된 경우 간단한 forgot-to-dispose() 버그로 인해 메모리가 손상될 수 있습니다.

이것은 과거에 finalize()가 사용되었던 사례 중 하나이며 아마도 그 단점 중 일부를 주도했을 것입니다.

다른 팁

먼저 질문을 오해했기 때문에 편집하십시오.

여기에서 인용 http://www.memorymanagement.org/glossary/p.html:

Java 사양에 따르면 참조 객체가 queueed시 팬텀 참조가 지워지지 않지만 실제로는 언어가 수행되었는지 여부를 알 수있는 방법이 없습니다.일부 구현에서 JNI 약한 글로벌 참조는 Phantom 참조보다 약하며 Phantom Reachable Object에 액세스 할 수있는 방법을 제공합니다.

그러나 나는 같은 말을 하는 다른 참고 자료를 찾지 못했습니다.

내 생각에는 다른 개체가 원래 개체가 수행하는 것 이상으로 추가 정리 작업을 수행하도록 하는 것이 아이디어라고 생각합니다.예를 들어 원본 객체를 확장하여 일부 마무리 작업을 구현할 수 없는 경우 팬텀 참조를 사용할 수 있습니다.

더 큰 문제는 JVM이 객체가 마무리될 것이라는 보장을 하지 않으며, 팬텀 참조가 마무리 후에 작업을 수행한다는 보장도 없다고 가정한다는 것입니다.

팬텀 참조는 리소스 해제와 같은 사전 가비지 수집 작업을 수행하는 데 사용될 수 있습니다.대신 사람들은 일반적으로 이를 위해 finalize() 메서드를 사용하는데 이는 좋은 생각이 아닙니다.종료자는 가비지 수집기의 성능에 끔찍한 영향을 미치며 "종료자"는 무작위 스레드에서 임의의 시간에 호출되므로 주의하지 않으면 애플리케이션의 데이터 무결성을 깨뜨릴 수 있습니다.

팬텀 참조의 생성자에서 참조된 개체가 "팬텀 연결 가능"이 되면 팬텀 참조가 대기열에 추가되는 ReferenceQueue를 지정합니다.팬텀 도달 가능이란 팬텀 참조를 통하지 않고는 도달할 수 없음을 의미합니다.처음에 혼란스러운 점은 팬텀 참조가 (소프트 또는 약한 참조와 달리) 개인 필드에서 참조된 객체를 계속 보유하지만 해당 getReference() 메서드는 항상 null을 반환한다는 것입니다.이는 개체를 다시 강력하게 연결할 수 없도록 하기 위한 것입니다.

때때로 ReferenceQueue를 폴링하고 참조된 개체가 팬텀 연결이 가능해진 새 PhantomReference가 있는지 확인할 수 있습니다.유용한 작업을 수행하려면 예를 들어 가비지 수집 전에 해제되어야 하는 리소스를 참조하는 java.lang.ref.PhantomReference에서 클래스를 파생시킬 수 있습니다.참조된 개체는 팬텀 참조 자체에 도달할 수 없게 된 후에만 가비지 수집됩니다.

http://www.javalobby.org/java/forums/m91822870.html#91822413

이는 수명주기 관리 메커니즘이 없지만 명시적인 수명주기 관리가 필요한 API를 구현하는 API를 위한 완벽한 솔루션입니다.

특히 메모리의 객체를 사용했지만 소켓 연결이나 다른 대규모 백업 저장소에 대한 파일 연결을 사용하여 다시 구현한 모든 종류의 API는 PhantomReference를 사용하여 연결 정보를 "닫고" 정리할 수 있습니다. 객체가 GC되고 연결이 닫히지 않습니다. 달리 사용할 수 있는 수명 주기 관리 API 인터페이스가 없었기 때문입니다.

간단한 지도 지도를 데이터베이스로 옮기는 것을 생각해 보세요.맵 참조가 삭제되면 명시적인 "닫기" 작업이 없습니다.그러나 캐시를 통한 쓰기를 구현했다면 모든 쓰기를 완료하고 "데이터베이스"에 대한 소켓 연결을 닫을 수 있기를 원할 것입니다.

다음은 이런 종류의 작업에 사용하는 클래스입니다.PhantomReferences에 대한 참조는 올바르게 작동하려면 로컬이 아닌 참조여야 합니다.그렇지 않으면 jit로 인해 코드 블록을 종료하기 전에 조기에 대기열에 추가됩니다.

    import java.lang.ref.PhantomReference;
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.logging.Level;
    import java.util.logging.Logger;

    /**
     * This class provides a way for tracking the loss of reference of one type of
     * object to allow a secondary reference to be used to perform some cleanup
     * activity.  The most common use of this is with one object which might
     * contain or refer to another object that needs some cleanup performed
     * when the referer is no longer referenced.
     * 

* 예제는 * 소켓 연결을 나타내거나 사용하는 유형 홀더의 객체 일 수 있습니다.기준이 손실되면 소켓을 닫아야합니다.따라서 인스턴스는 *에서와 같이 생성 될 수 있습니다.

     *    ReferenceTracker trker = ReferenceTracker() {
     *        public void released( Socket s ) {
     *            try {
     *                s.close();
     *            } catch( Exception ex ) {
     *                log.log( Level.SEVERE, ex.toString(), ex );
     *            }
     *        }
     *  };
     * 
* Somewhere, there might be calls such as the following. *
     *        interface Holder {
     *            public T get();
     *        }
     *        class SocketHolder implements Holder {
     *            Socket s;
     *            public SocketHolder( Socket sock ) {
     *                s = sock;
     *            }
     *            public Socket get() {
     *                return s;
     *            }
     *        }
     * 
* This defines an implementation of the Holder interface which holds * a reference to Socket objects. The use of the trker * object, above, might then include the use of a method for creating * the objects and registering the references as shown below. *
     *    public SocketHolder connect( String host, int port ) throws IOException {
     *        Socket s = new Socket( host, port );
     *        SocketHolder h = new SocketHolder( s );
     *        trker.trackReference( h, s );
     *        return h;
     *    }
     * 
* Software wishing to use a socket connection, and pass it around would * use SocketHolder.get() to reference the Socket instance, in all cases. * then, when all SocketHolder references are dropped, the socket would * be closed by the released(java.net.Socket) method shown * above. *

* {@link referenceTracker} 클래스는 {@link phantomreference}를 첫 번째 인수에 사용하여 두 번째 인수를 참조하는 맵의 키입니다.따라서 * 키 인스턴스가 릴리스되면 키 참조를 대기시켜 큐에서 제거하고 큐에서 제거 할 수 있으며 맵에서 값을 제거한 다음 * release ()로 전달됩니다.* / public Abstract Class ReferenceTracker { / *** 참조 큐에서 항목을 제거하는 스레드 인스턴스가 나타나는대로.*/ 개인 휘발성 refqueuepoll 여론 조사;/***이 인스턴스에 사용 된 로거 인스턴스.해당 생성자가 사용되는 경우 이름을 접미사 *로 포함합니다.*/ private static final logger log = logger.getLogger (referenceTracker.class.getName ());/** *이 인스턴스가 로깅 및 기타 필요한 인스턴스를 나타내는 인스턴스를 나타내는 이름입니다.*/ 개인 최종 문자열;/** * 통과 된 이름을 사용하여 새로운 인스턴스 인스턴스를 생성하여 로깅 및 toString () 구현의 인스턴스를 차별화합니다.* @param which 로깅 등에서 여러 인스턴스를 구별하기 위한 이 인스턴스의 이름입니다.*/ public referenceTracker (string who) {this.hwich = what;} /*** 자격이없는 이름이없는 새로운 인스턴스 인스턴스를 만듭니다.*/ public referenceTracker () {this.which = null;} /***이 인스턴스의 이름에 대한 액세스를 제공합니다.* @return 이 인스턴스의 이름입니다.*/ @override public String toString () {if (whit == null) {return super.toString ()+":참조 추적기";} return super.toString ()+":ReferenceTracker["+which+"]";} /*** 서브 클래스는이 메소드를 구현해야합니다.* 관련 홀더 객체에 대한 모든 참조가 삭제 될 때 호출됩니다.* @param val {@link #trackreference (Object, Object) TrackReference (t, k)} */ public Abstract void Releasion (k val)에 대한 해당 호출에 대한 두 번째 인수로 전달 된 값;/ ** 홀더 객체에 대한 참조 큐*/ 개인 최종 참조 QuiceUerefqueue = 새로운 참조 Queue ();/** * 항목이 생성 된 후 파괴 된 총 스레드 수의 수가 추적되었습니다.추적된 참조가 0이면 실행 중인 대기열이 없습니다.*/ Private Final AtomicInteger tcnt = new atomicinteger ();개인용 휘발성 부울 실행;/** * {@link #refqueue}를 조사하여 T 객체에 대한 참조가 삭제 될 때 {@link releasion (k)} *를 호출하는 스레드 구현.* / private class refqueuepoll은 스레드 { / ***이 인스턴스와 관련된 스레드 번호를 확장합니다.휘발성 시스템에 존재하는이 클래스의 두 가지 인스턴스가있을 수 있습니다.이 경우,이 값은 활성 값을 차별화하기 위해 일부 로깅에서 볼 수 있습니다.*/ 개인 최종 int mycnt;/***이 클래스의 인스턴스를 만듭니다.*/ public refqueuepoll () {setdaemon (true);setName( getClass().getName()+":ReferenceTracker ("+which+")" );mycnt = tcnt.incrementAndGet();} /***이 방법은 수행의 모든 ​​활동을 제공합니다. refqueue.remove()* 전화를 걸고 전화를 걸다 released(K) 응용 프로그램이 필요한 리소스를 해제하도록합니다.*/ public @override void run () {try {dorun ();} catch (Throwable ex) {log.log (완료?레벨.정보:level.severe, ex.tostring ()+":팬텀 참조 폴 스레드 중지", ex );} 마침내 {running = false;}} 개인 휘발성 부울 완료 = 거짓;private void dorun () {while (! done) {참조 ref = null;{running = true;ref = refqueue.remove();Kctl;동기화 (refmap) {ctl = refmap.remove (ref);완료 = actCnt.decrementAndGet() == 0;if (log.isloggable (level.fine)) {log.log (level.fine, "current act refs = {0}, mapsize = {1}", new Object [] {actcnt.get (), refmap.size ()});} if (actcnt.get ()! = refMap.size ()) {Throwable ex = new ImperalStateException ( "활성 참조 수 및 맵 크기는 동기화되지 않음");log.log(Level.SEVERE, ex.toString(), ex);}} if (log.isloggable (level.finer)) {log.log (level.finer, "참조 릴리스 :{0}, dep={1}", new Object[]{ref, ctl});} if (ctl! = null) {try {releasion (ctl);if (log.isloggable (level.fine)) {log.log (level.fine, "종속 객체 릴리스 :{0}", ctl);}} catch (runtimeexception ex) {log.log (level.severe, ex.tostring (), ex);}}} catch (예외 예) {log.log (level.severe, ex.tostring (), ex);} 마침내 {if (ref! = null) {ref.clear ();}}} if (log.isloggable (level.fine))) {log.log (level.fine, "{1}"에 대한 shutdown, new Object [] {mycnt, this});}}} /*** 활성 참조의 카운트.*/ Private Final AtomicInteger actcnt = new atomicinteger ();/ ** * release (k) 호출에 사용될 k 객체에 대한 t 참조의 맵 (k) 호출 */ private final concurrenthashmap, k> refmap = new ConcurrenthashMap, k> ();/*** 추적 참조를 추가합니다.DEP는 약한 회의를 제외하고 어떤 식 으로든 심판을 언급해서는 안됩니다.dep는 거의 항상 ref로 참조되는 것입니다.* ref와 dep의 @throws IllegalArgumentException은 동일한 객체입니다.* @param dep ref가 더 이상 참조되지 않을 때 정리가 필요한 종속 개체입니다.* @param ref 참조를 추적 할 객체 */ public void trackreference (t ref, k dep) {if (ref == dep) {새로운 불법 불법 행위 exception ( "참조 객체 및 종속 객체가 동일 할 수 없음") ;} phantomreference p = 새로운 phantomreference (ref, refqueue);동기화 (refmap) {refmap.put (p, dep);if (actCnt.getAndIncrement () == 0 || running == false) {if (actcnt.get ()> 0 && running == false) {if (log.isloggable (level.fine)) {log.fine (log.fine) "팬텀 심판 폴링 스레드 중지 시작");}} poll = 새로운 refqueuepoll ();poll.start();if (log.isloggable (level.fine)) {log.log (level.fine, "폴링 스레드 #{0}, {1}", new Object [] {tcnt.get (), this});}}}}} /** *이 메소드는 트래커가있는 JVM이 종료 중이거나 일부 컨텍스트가 종료되고 추적자별로 추적 *을 해제하는 경우이 메소드를 호출 할 수 있습니다.이 방법은 * {@link #Released (Object) release (k)} 각각의 미결제 추천에 대해 호출됩니다.*/ public void shutdown () {listrem;// 값을 복사하고 맵을 제거하여 release가 한 번만 호출되도록 맵을 지 웁니다.refmap.clear();} for (k dep :rem) {try {releasion (dep);} catch (예외 예) {log.log (level.severe, ex.tostring (), ex);}}}}

이를 통해 두 사람은 메모리 관리에 매우 효율적인 팬텀 캐시를 가질 수 있습니다.간단히 말해서, 생성하는 데 비용이 많이 들지만 거의 사용되지 않는 거대한 개체가 있는 경우 팬텀 캐시를 사용하여 해당 개체를 참조하고 더 귀중한 메모리를 차지하지 않도록 할 수 있습니다.일반 참조를 사용하는 경우 개체에 대한 참조가 남아 있지 않은지 수동으로 확인해야 합니다.모든 개체에 대해 동일한 주장을 할 수 있지만 팬텀 캐시의 참조를 수동으로 관리할 필요는 없습니다.수집되었는지 여부를 주의 깊게 확인해야 합니다.

또한 프레임워크를 사용할 수도 있습니다(예:팩토리) 참조가 팬텀 참조로 제공되는 경우.이는 객체가 많고 수명이 짧은 경우에 유용합니다(예:사용 후 폐기).가비지 수집이 마법이라고 생각하는 엉성한 프로그래머가 있는 경우 메모리를 지우는 데 매우 유용합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top