代码之家  ›  专栏  ›  技术社区  ›  Shimi Bandiel

幻象参照对象

  •  8
  • Shimi Bandiel  · 技术社区  · 18 年前

    幻影参考用于尸检操作。 幻象参照对象 在清理幻影引用本身之前,不会解除分配。

    (我提出的唯一想法是,允许本机代码对对象进行事后清理,但这不太令人信服)。

    6 回复  |  直到 12 年前
        1
  •  2
  •   jrudolph    18 年前

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

    http://www.memorymanagement.org/glossary/p.html :

    Java规范说 幻影参考在以下情况下不清除: 事实上,这是不可能的 JNI弱全局引用较弱 比幻影参考,并提供 可访问幻影的方法 物体。

        2
  •  1
  •   Ed Staub    14 年前

    这是过去使用finalize()的情况之一,可能导致了它的一些怪癖。

        3
  •  1
  •   Zarkonnen    18 年前

        4
  •  1
  •   James A. N. Stauffer    18 年前
    幻象引用可用于执行预垃圾收集操作,如释放资源。相反,人们通常为此使用finalize()方法,这不是一个好主意。终结器会对垃圾收集器的性能产生可怕的影响,如果您不十分小心,可能会破坏应用程序的数据完整性,因为“终结器”是在随机线程中随机调用的。

    在幻影引用的构造函数中,指定一个ReferenceQueue,一旦引用的对象变为“幻影可访问”,幻影引用将在该队列中排队。幻影可达性是指通过幻影引用以外的其他方式不可达。最初令人困惑的是,尽管phantom引用继续将被引用对象保存在私有字段中(与软引用或弱引用不同),但其getReference()方法始终返回null。这是因为您无法使对象再次强可访问。

    您可以不时轮询ReferenceQueue,并检查是否存在任何新的PhantomReferences,这些PhantomReferences的引用对象已成为Phantoma可访问的对象。为了能够访问任何有用的内容,例如,可以从java.lang.ref.PhantomReference派生一个类,该类引用在垃圾收集之前应该释放的资源。只有当幻影引用本身变得不可访问时,才会对引用对象进行垃圾收集。

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

        5
  •  0
  •   Grwww    9 年前

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

    考虑将一个简单的地图移动到数据库中。放弃映射引用时,没有显式的“关闭”操作。然而,如果您已经实现了直写缓存,那么您希望能够完成任何写操作并关闭到“数据库”的套接字连接。

        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 ); } } } }

        6
  •  -2
  •   Javaxpert    18 年前

    它可以让你们两个有幻影缓存,这是非常有效的内存管理。 简单地说,如果您有创建成本很高但很少使用的大型对象,您可以使用幻影缓存来引用它们,并确保它们不会占用更有价值的内存。如果使用常规引用,则必须手动确保没有对对象的引用。您可以对任何对象进行相同的争论,但不必手动管理幻影缓存中的引用。只是要仔细检查他们是否被收集了。

    此外,您还可以使用一个框架(即工厂),其中引用作为虚拟引用提供。如果对象数量多且寿命短(即使用后处理),则此功能非常有用。如果你的程序员认为垃圾收集很神奇,那么清理内存非常方便。