幻影参考对象

时间:2008-09-17 07:06:25

标签: java finalize phantom-reference

Phantom References用于验尸操作。 Java规范规定,在清除幻像引用本身之前,不会释放幻像引用对象

我的问题是:此功能(未解除分配的对象)的用途是什么?

(我提出的唯一想法是允许本机代码对对象进行事后清理,但这并不是很有说服力。)

6 个答案:

答案 0 :(得分:2)

编辑,因为我首先误解了这个问题:

从这里引用http://www.memorymanagement.org/glossary/p.html

  

Java规范说明了   幻像参考不会被清除   引用对象已入队,但是   实际上,没有办法了   用语言判断是否存在   完成与否。在一些实现中,   JNI弱全球参考较弱   比幻像参考,并提供一个   访问幻像可达的方式   对象。

但我发现没有其他参考文献会这样说。

答案 1 :(得分:1)

我认为这个想法是让其他对象在原始对象之外进行额外的清理。例如,如果原始对象无法扩展以实现某些终结内容,则可以使用幻像引用。

更大的问题是JVM不能保证对象永远不会被最终确定,并且我不能保证幻像引用可以在完成后完成它们。

答案 2 :(得分:1)

幻像引用可用于执行预垃圾收集操作,例如释放资源。相反,人们通常使用finalize()方法,这不是一个好主意。终结器会对垃圾收集器的性能产生可怕的影响,并且如果您不是非常小心,可能会破坏应用程序的数据完整性,因为随机时间会在随机时间调用“终结器”。
/> 在幻像引用的构造函数中,指定一个ReferenceQueue,一旦引用的对象变为“幻像可达”,幻像引用就会入队。幻影可达除了通过幻像参考之外无法到达。最初令人困惑的是,虽然幻像引用继续将引用的对象保存在私有字段中(与软引用或弱引用不同),但其getReference()方法始终返回null。这样就无法再次强烈地触及对象了。

有时,您可以轮询ReferenceQueue并检查是否有任何新的PhantomReferences,其引用的对象已变为幻像可达。为了能够使用任何有用的东西,例如可以从java.lang.ref.PhantomReference派生一个类,该类引用在垃圾收集之前应该释放的资源。只有在幻像引用变得无法访问时,引用的对象才会被垃圾收集。

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

答案 3 :(得分:1)

我能想到的唯一一个可以防止重新分配的好用例是某种JNI实现的异步数据源写入引用对象,并且必须被告知要停止写入 - 停止写入对象 - 在回收内存之前。如果允许先前的释放,则一个简单的忘记处置()错误可能导致内存损坏。

这是过去使用finalize()的情况之一,可能会引发一些怪癖。

答案 4 :(得分:0)

对于没有生命周期管理机制的API,这是一个完美的解决方案,但是您正在使用需要明确生命周期管理的东西来实现。

特别是任何类型的API,过去只使用内存中的对象,但是您使用套接字连接或文件连接重新实现到其他更大的后备存储,可以使用PhantomReference来关闭"关闭& #34;并且在对象为GC之前清理连接信息并且连接从未关闭,因为没有您可以使用的生命周期管理API接口。

考虑将简单的地图映射移动到数据库中。当地图参考被丢弃时,没有明确的"关闭"操作。但是,如果您已经实现了写入缓存,那么您希望能够完成任何写入并关闭与您的数据库的套接字连接,并且#34;。

以下是我用于此类内容的课程。注意,对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.
     * 

* An example might be an object of type Holder, which refers to or uses a * Socket connection. When the reference is lost, the socket should be * closed. Thus, an instance might be created as in *

     *    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. *

* The {@link ReferenceTracker} class uses a {@link PhantomReference} to the first argument as * the key to a map holding a reference to the second argument. Thus, when the * key instance is released, the key reference is queued, can be removed from * the queue, and used to remove the value from the map which is then passed to * released(). */ public abstract class ReferenceTracker { /** * The thread instance that is removing entries from the reference queue, refqueue, as they appear. */ private volatile RefQueuePoll poll; /** * The Logger instance used for this instance. It will include the name as a suffix * if that constructor is used. */ private static final Logger log = Logger.getLogger(ReferenceTracker.class.getName()); /** * The name indicating which instance this is for logging and other separation of * instances needed. */ private final String which; /** * Creates a new instance of ReferenceTracker using the passed name to differentiate * the instance in logging and toString() implementation. * @param which The name of this instance for differentiation of multiple instances in logging etc. */ public ReferenceTracker( String which ) { this.which = which; } /** * Creates a new instance of ReferenceTracker with no qualifying name. */ public ReferenceTracker( ) { this.which = null; } /** * Provides access to the name of this instance. * @return The name of this instance. */ @Override public String toString() { if( which == null ) { return super.toString()+": ReferenceTracker"; } return super.toString()+": ReferenceTracker["+which+"]"; } /** * Subclasses must implement this method. It will be called when all references to the * associated holder object are dropped. * @param val The value passed as the second argument to a corresponding call to {@link #trackReference(Object, Object) trackReference(T,K)} */ public abstract void released( K val ); /** The reference queue for references to the holder objects */ private final ReferenceQueuerefqueue = new ReferenceQueue(); /** * The count of the total number of threads that have been created and then destroyed as entries have * been tracked. When there are zero tracked references, there is no queue running. */ private final AtomicInteger tcnt = new AtomicInteger(); private volatile boolean running; /** * A Thread implementation that polls {@link #refqueue} to subsequently call {@link released(K)} * as references to T objects are dropped. */ private class RefQueuePoll extends Thread { /** * The thread number associated with this instance. There might briefly be two instances of * this class that exists in a volatile system. If that is the case, this value will * be visible in some of the logging to differentiate the active ones. */ private final int mycnt; /** * Creates an instance of this class. */ public RefQueuePoll() { setDaemon( true ); setName( getClass().getName()+": ReferenceTracker ("+which+")" ); mycnt = tcnt.incrementAndGet(); } /** * This method provides all the activity of performing refqueue.remove() * calls and then calling released(K) to let the application release the * resources needed. */ public @Override void run() { try { doRun(); } catch( Throwable ex ) { log.log( done ? Level.INFO : Level.SEVERE, ex.toString()+": phantom ref poll thread stopping", ex ); } finally { running = false; } } private volatile boolean done = false; private void doRun() { while( !done ) { Reference ref = null; try { running = true; ref = refqueue.remove(); K ctl; synchronized( refmap ) { ctl = refmap.remove( ref ); done = 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 IllegalStateException("count of active references and map size are not in sync"); log.log(Level.SEVERE, ex.toString(), ex); } } if( log.isLoggable( Level.FINER ) ) { log.log(Level.FINER, "reference released for: {0}, dep={1}", new Object[]{ref, ctl}); } if( ctl != null ) { try { released( ctl ); if( log.isLoggable( Level.FINE ) ) { log.log(Level.FINE, "dependant object released: {0}", ctl); } } catch( RuntimeException ex ) { log.log( Level.SEVERE, ex.toString(), ex ); } } } catch( Exception ex ) { log.log( Level.SEVERE, ex.toString(), ex ); } finally { if( ref != null ) { ref.clear(); } } } if( log.isLoggable( Level.FINE ) ) { log.log(Level.FINE, "poll thread {0} shutdown for {1}", new Object[]{mycnt, this}); } } } /** * A count of the active references. */ private final AtomicInteger actCnt = new AtomicInteger(); /** * Map from T References to K objects to be used for the released(K) call */ private final ConcurrentHashMap,K>refmap = new ConcurrentHashMap,K>(); /** * Adds a tracked reference. dep should not refer to ref in any way except possibly * a WeakReference. dep is almost always something referred to by ref. * @throws IllegalArgumentException of ref and dep are the same object. * @param dep The dependent object that needs cleanup when ref is no longer referenced. * @param ref the object whose reference is to be tracked */ public void trackReference( T ref, K dep ) { if( ref == dep ) { throw new IllegalArgumentException( "Referenced object and dependent object can not be the same" ); } PhantomReference p = new PhantomReference( ref, refqueue ); synchronized( refmap ) { refmap.put( p, dep ); if( actCnt.getAndIncrement() == 0 || running == false ) { if( actCnt.get() > 0 && running == false ) { if (log.isLoggable(Level.FINE)) { log.fine("starting stopped phantom ref polling thread"); } } poll = new RefQueuePoll(); poll.start(); if( log.isLoggable( Level.FINE ) ) { log.log( Level.FINE, "poll thread #{0} created for {1}", new Object[]{tcnt.get(), this}); } } } } /** * This method can be called if the JVM that the tracker is in, is being * shutdown, or someother context is being shutdown and the objects tracked * by the tracker should now be released. This method will result in * {@link #released(Object) released(K) } being called for each outstanding refernce. */ public void shutdown() { Listrem; // Copy the values and clear the map so that released // is only ever called once, incase GC later evicts references synchronized( refmap ) { rem = new ArrayList( refmap.values() ); refmap.clear(); } for( K dep : rem ) { try { released( dep ); } catch( Exception ex ) { log.log( Level.SEVERE, ex.toString(), ex ); } } } }

答案 5 :(得分:-2)

它可以让你有两个幻像缓存,它们在内存管理方面非常有效。 简单地说,如果你有很多巨大的对象来创建但很少使用,你可以使用幻像缓存来引用它们,并确保它们不会占用更有价值的内存。如果使用常规引用,则必须手动确保对象没有任何引用。您可以对任何对象进行相同的争论,但您不必手动管理幻像缓存中的引用。只需要小心检查它们是否已被收集。

此外,您可以使用框架(即工厂),其中引用作为幻像引用。如果对象很多且寿命很短(即使用然后处理),这将非常有用。如果你有邋py的程序员认为垃圾收集是神奇的,那么清理内存非常方便。