幽灵引用用于死后操作。
Java规范指出,幽灵引用对象将不会被释放,直到幽灵引用本身被清除。
我的问题是:这个特性(对象不被释放)有什么作用?
(我唯一想到的想法是允许本地代码对对象进行死后清理,但这并不太令人信服。)
幽灵引用用于死后操作。
Java规范指出,幽灵引用对象将不会被释放,直到幽灵引用本身被清除。
我的问题是:这个特性(对象不被释放)有什么作用?
(我唯一想到的想法是允许本地代码对对象进行死后清理,但这并不太令人信服。)
编辑,因为我之前误解了问题:
引用自这里http://www.memorymanagement.org/glossary/p.html:
Java规范说明虚引用在引用对象入队时不会被清除,但实际上,在语言中无法确定是否已经执行了该操作。在某些实现中,JNI弱全局引用比虚引用更弱,并提供了一种访问虚可达对象的方法。
但我没有找到其他说同样话的参考资料。
我认为这个想法是让其他对象在原始对象之上进行额外的清理工作。例如,如果原始对象无法扩展以实现一些终结工作,您可以使用虚引用。
更大的问题是JVM不能保证对象会被终结,我假设在此基础上也不能保证虚引用在终结后执行它们的任务。
http://www.javalobby.org/java/forums/m91822870.html#91822413
这是一个完美的解决方案,适用于没有生命周期管理机制但需要使用显式生命周期管理的API。
特别是任何使用内存中对象的API,但是您重新实现了使用套接字连接或文件连接到其他较大后备存储的API,可以使用PhantomReference在对象被GC并且未关闭连接因为没有生命周期管理API接口的情况下,“关闭”和清理连接信息。
想象一下将简单的Map映射到数据库中。当Map引用被丢弃时,没有明确的“关闭”操作。但是,如果您已实施写入缓存,您希望能够完成任何写入并关闭到您的“数据库”的套接字连接。
以下是我用于此类操作的类。请注意,对于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;
/** * 此类提供了一种跟踪某种对象引用丢失的方法,以允许使用次要引用执行一些清理活动。最常见的用法是一个对象可能包含或引用另一个需要在引用者不再被引用时执行一些清理操作的对象。 ** 例如,可能会有一个类型为Holder的对象,它引用或使用Socket连接。当引用丢失时,应关闭套接字。因此,可以创建一个实例,如下所示: *
* ReferenceTracker trker = ReferenceTracker() { * public void released( Socket s ) { * try { * s.close(); * } catch( Exception ex ) { * log.log( Level.SEVERE, ex.toString(), ex ); * } * } * }; ** 然后,在某个地方,可能会调用以下方法。 ** interface Holder { * public T get(); * } * class SocketHolder implements Holder { * Socket s; * public SocketHolder( Socket sock ) { * s = sock; * } * public Socket get() { * return s; * } * } ** 这定义了一个Holder接口的实现,它持有对Socket对象的引用。上面的trker
对象的使用可能还包括使用以下方法来创建对象并注册引用。 ** 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; * } ** 希望使用套接字连接并传递它的软件将使用SocketHolder.get()在所有情况下引用Socket实例。然后,当所有SocketHolder引用都被删除时,套接字将通过上面显示的released(java.net.Socket)
方法关闭。 ** {@link ReferenceTracker}类使用{@link PhantomReference}作为第一个参数的键,以保留对第二个参数的引用。因此,当释放关键实例时,可以将关键引用排队,可以从队列中删除,并用于从映射中删除值,然后将其传递给released()。 */ public abstract class ReferenceTracker { /** * 从引用队列refqueue中移除条目的线程实例。 */ private volatile RefQueuePoll poll; /** * 此实例用于此实例的Logger。如果使用该构造函数,则它将包含名称作为后缀。 */ private static final Logger log = Logger.getLogger(ReferenceTracker.class.getName()); /** * 表示此实例的名称,以进行日志记录和其他所需的实例分离。 */ private final String which;
/** * 使用传递的名称创建ReferenceTracker的新实例,以区分日志记录和toString()实现中的实例。 * @param which 用于在日志记录等中区分多个实例的此实例的名称。 */ public ReferenceTracker( String which ) { this.which = which; }
/** * 创建没有限定名称的ReferenceTracker的新实例。 */ public ReferenceTracker( ) { this.which = null; }
/** * 提供对此实例名称的访问。 * @return 此实例的名称。 */ @Override public String toString() { if( which == null ) { return super.toString()+": ReferenceTracker";
它可以让您拥有幻影缓存,这在内存管理方面非常高效。 简单来说,如果您有巨大的对象,创建成本很高但很少使用,您可以使用幻影缓存引用它们,并确保它们不占用更有价值的内存。如果您使用常规引用,则必须手动确保没有对该对象的引用。您可以对任何对象提出同样的论点,但是您不必手动管理幻影缓存中的引用。只需小心检查它们是否已被收集即可。
此外,您可以使用框架(即工厂),其中引用被视为幻影引用。如果对象很多且寿命短暂(即使用后即处置),则这非常有用。如果您有粗心的程序员认为垃圾回收是神奇的,则非常方便清除内存。