虚拟引用对象

发布于 2024-07-05 13:55:33 字数 140 浏览 10 评论 0原文

虚拟参考用于事后分析操作。 Java 规范规定,在幻像引用本身被清理之前,幻像引用对象不会被释放。

我的问题是:此功能(未释放对象)的用途是什么?

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

Phantom References serve for post-mortem operations.
The Java specification states that a phantom referenced object will not be deallocated until the phantom-reference itself is cleaned.

My question is: What purpose does this feature (object not deallocated) serve?

(The only idea i came up with, is to allow native code to do post-mortem cleanup on the object, but it isn't much convincing).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

电影里的梦 2024-07-12 13:55:34

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

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

Java 规范指出
幻象引用在以下情况下不会被清除
引用对象已排队,但是
事实上,没有办法
语言来判断是否已经
完成与否。 在一些实施方式中,
JNI弱全局引用更弱
比虚拟引用,并提供
访问幻象可达的方式
对象。

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

Edit, since I've misunderstand the question first:

Quoted from here http://www.memorymanagement.org/glossary/p.html:

The Java specification says that the
phantom reference is not cleared when
the reference object is enqueued, but
actually, there's no way in the
language to tell whether that has been
done or not. In some implementations,
JNI weak global references are weaker
than phantom references, and provide a
way to access phantom reachable
objects.

But I found no other references which would say the same.

定格我的天空 2024-07-12 13:55:34

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

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

The only good use-case I can think of, that would prevent deallocation, is one where some kind of JNI-implemented asynchronous data source is writing into the referenced object, and must be told to stand down - to stop writing into the object - before the memory is recycled. If prior deallocation were allowed, a simple forgot-to-dispose() bug could result in memory corruption.

This is one of the cases where finalize() would have been used in the past, and probably drove some of its quirks.

Bonjour°[大白 2024-07-12 13:55:34

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

更大的问题是 JVM 不保证对象将被最终确定,并且我认为扩展后也不能保证幻像引用在最终确定后执行其操作。

I think the idea is to let other objects do extra cleanup above and beyond what the original object does. For example, if the original object cannot be extended to implement some finalization stuff, you can use phantom references.

The bigger problem is that the JVM makes no guarantee that an object will ever be finalized, and I assume by extension no guarantee that phantom references get to do their thing post-finalization.

乖不如嘢 2024-07-12 13:55:34

幻像引用可用于执行预垃圾收集操作,例如释放资源。 相反,人们通常使用 Finalize() 方法来实现此目的,这不是一个好主意。 终结器会对垃圾收集器的性能产生可怕的影响,并且如果您不非常小心,可能会破坏应用程序的数据完整性,因为“终结器”是在随机线程中随机调用的。

在幻影引用的构造函数中,您指定一个 ReferenceQueue,一旦引用的对象变为“幻影可到达”,幻影引用就会在其中排队。 虚拟可访问意味着除了通过虚拟引用之外无法访问。 最初令人困惑的是,尽管幻像引用继续将引用的对象保存在私有字段中(与软引用或弱引用不同),但其 getReference() 方法始终返回 null。 这样您就无法再次使对象强可达。

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

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

Phantom references can be used to perform pre-garbage collection actions such as freeing resources. Instead, people usually use the finalize() method for this which is not a good idea. Finalizers have a horrible impact on the performance of the garbage collector and can break data integrity of your application if you're not very careful since the "finalizer" is invoked in a random thread, at a random time.

In the constructor of a phantom reference, you specify a ReferenceQueue where the phantom references are enqueued once the referenced objects becomes "phantom reachable". Phantom reachable means unreachable other than through the phantom reference. The initially confusing thing is that although the phantom reference continues to hold the referenced object in a private field (unlike soft or weak references), its getReference() method always returns null. This is so that you cannot make the object strongly reachable again.

From time to time, you can poll the ReferenceQueue and check if there are any new PhantomReferences whose referenced objects have become phantom reachable. In order to be able to to anything useful, one can for example derive a class from java.lang.ref.PhantomReference that references resources that should be freed before garbage collection. The referenced object is only garbage collected once the phantom reference becomes unreachable itself.

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

傲鸠 2024-07-12 13:55:34

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

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

考虑将简单的 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;

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

* 一个例子可能是 Holder 类型的对象,它引用或使用 * 套接字连接。 当参考丢失时,套接字应该是 * 关闭。 因此,可以创建一个实例,如下所示 *

     *    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} 作为
* 保存对第二个参数的引用的映射的键。 因此,当
* key实例被释放,key引用被排队,可以从中删除
* 队列,用于从映射中删除值,然后将其传递给
* 发布()。
*/
公共抽象类ReferenceTracker {
/**
* 从引用队列 refqueue 中删除条目的线程实例(如它们出现的那样)。
*/
私有易失性 RefQueuePoll 轮询;
/**
* 用于此实例的 Logger 实例。 它将包含名称作为后缀
* 如果使用该构造函数。
*/
私有静态最终 Logger log = Logger.getLogger(ReferenceTracker.class.getName());
/**
* 名称表明这是哪个实例用于日志记录和其他分离
* 需要实例。
*/
私有最终字符串其中;

/**
* 使用传递的名称创建ReferenceTracker的新实例来区分
* 日志记录和 toString() 实现中的实例。
* @param which 该实例的名称,用于在日志记录等中区分多个实例。
*/
公共ReferenceTracker(字符串其中){
这.which = 哪个;
}

/**
* 创建一个没有限定名称的 ReferenceTracker 的新实例。
*/
公共参考跟踪器(){
这.which = null;
}

/**
* 提供对此实例名称的访问。
* @return 该实例的名称。
*/
@覆盖
公共字符串 toString() {
如果(其中 == null ){
return super.toString()+": ReferenceTracker";
}
返回 super.toString()+": ReferenceTracker["+which+"]";
}

/**
* 子类必须实现该方法。 当所有引用都被调用时,它将被调用
* 关联的持有者对象被删除。
* @param val 作为第二个参数传递给对 {@link #trackReference(Object, Object) trackReference(T,K)} 的相应调用的值
*/
公共抽象无效释放(K val);

/** 持有者对象引用的引用队列 */
私有最终ReferenceQueuerefqueue = new ReferenceQueue();
/**
* 已创建然后销毁的线程总数的计数
* 被追踪。 当跟踪引用为零时,就没有队列在运行。
*/
私有最终 AtomicInteger tcnt = new AtomicInteger();
私有易失性布尔运算;
/**
* 轮询 {@link #refqueue} 以随后调用 {@linkreleased(K)} 的线程实现
* 因为对 T 对象的引用被删除。
*/
私有类 RefQueuePoll 扩展 Thread {
/**
* 与该实例关联的线程号。 可能会短暂地出现两个实例
* 此类存在于易失性系统中。 如果是这种情况,该值将
* 在某些日志记录中可见,以区分活动的日志记录。
*/
私人最终 int mycnt;
/**
* 创建该类的一个实例。
*/
公共 RefQueuePoll() {
setDaemon( 真 );
setName( getClass().getName()+": ReferenceTracker("+which+")" );
mycnt = tcnt.incrementAndGet();
}
/**
* 该方法提供执行refqueue.remove()的所有活动
* 调用然后调用released(K)让应用程序释放
* 需要的资源。
*/
公共@Override无效运行(){
尝试 {
doRun();
} catch( 可抛出的 ex ) {
log.log( 完成? Level.INFO : Level.SEVERE,
ex.toString()+": 幻影引用轮询线程停止", ex );
} 最后 {
运行=假;
}
}

私人易失性布尔完成=假;
私有无效 doRun() {
while(!完成) {
参考 ref = null;
尝试 {
运行=真;
ref = refqueue.remove();
K控制;
同步( refmap ) {
ctl = refmap.remove( ref );
完成 = actCnt.decrementAndGet() == 0;
if( log.isLoggable( Level.FINE ) ) {
log.log(Level.FINE, "当前行为 refs={0}, mapsize={1}", new Object[]{actCnt.get(), refmap.size()});
}
if( actCnt.get() != refmap.size() ) {
Throwable ex = new IllegalStateException("活动引用计数和地图大小不同步");
log.log(Level.SEVERE, ex.toString(), ex);
}
}

if( log.isLoggable( Level.FINER ) ) {
log.log(Level.FINER, "引用已发布:{0}, dep={1}", new Object[]{ref, ctl});
}
如果(ctl!= null){
尝试 {
释放(ctl);
if( log.isLoggable( Level.FINE ) ) {
log.log(Level.FINE, "依赖对象已释放:{0}", ctl);
}
} catch(运行时异常前){
log.log( Level.SEVERE, ex.toString(), ex );
}
}

} catch(异常前){
log.log( Level.SEVERE, ex.toString(), ex );
} 最后 {
如果(参考!=空){
ref.clear();
}
}
}

if( log.isLoggable( Level.FINE ) ) {
log.log(Level.FINE, "轮询线程 {0} 关闭 {1}", new Object[]{mycnt, this});
}
}
}

/**
* 活动引用的计数。
*/
私有最终 AtomicInteger actCnt = new AtomicInteger();
/**
* 从T引用映射到用于release(K)调用的K个对象
*/
private Final ConcurrentHashMap,K>refmap = new ConcurrentHashMap,K>();
/**
* 添加跟踪参考。 dep 不应以任何方式引用 ref,除非可能
* 弱引用。 dep 几乎总是由 ref 引用的东西。
* @throws IllegalArgumentException ref 和 dep 是同一个对象。
* @param dep 当 ref 不再被引用时需要清理的依赖对象。
* @param ref 要跟踪其引用的对象
*/
公共无效trackReference(T ref,K dep){
if( ref == dep ) {
throw new IllegalArgumentException( "引用对象和依赖对象不能相同" );
}
PhantomReference p = new PhantomReference( ref, refqueue );
同步( refmap ) {
refmap.put( p, dep );
if( actCnt.getAndIncrement() == 0 || 运行 == false ) {
if( actCnt.get() > 0 && 运行 == false ) {
if (log.isLoggable(Level.FINE)) {
log.fine(“开始停止幻象引用轮询线程”);
}
}
轮询 = new RefQueuePoll();
轮询.start();
if( log.isLoggable( Level.FINE ) ) {
log.log( Level.FINE, "为 {1} 创建的轮询线程 #{0}", new Object[]{tcnt.get(), this});
}
}
}
}

/**
* 如果跟踪器所在的 JVM 正在被调用,则可以调用此方法
* 关闭,或者其他上下文正在关闭并且对象被跟踪
* 由跟踪器现在应该被释放。 该方法将导致
* {@link #released(Object)released(K)} 为每个未完成的引用调用。
*/
公共无效关闭(){
利斯特雷姆;
// 复制值并清除地图以便释放
// 只被调用一次,以防 GC 稍后驱逐引用
同步( refmap ) {
rem = new ArrayList( refmap.values() );
refmap.clear();
}
for( K dep : rem ) {
尝试 {
发布( dep );
} catch(异常前){
log.log( Level.SEVERE, ex.toString(), ex );
}
}
}
}

This is a perfect solution for APIs which don't have a lifecycle management mechanism, but which you are implementing with something which requires explicit lifecycle management.

In particular any sort of API which used to just use objects in memory, but which you've reimplemented using a socket connection or file connection to some other, larger backing store, can use PhantomReference to "close" and cleanup connection information prior to the object being GC'd and the connection never closed because there was no lifecycle management API interface that you could otherwise use.

Think of moving a simple Map map into a database. When the map reference is discarded, there is no explicit "close" operation. Yet, if you had implemented a write through cache, you'd like to be able to finish any writes and close the socket connection to the your "database".

Below is a class which I use for this kind of stuff. Note, that References to PhantomReferences must be non-local references to work correctly. Otherwise, the jit will cause them to be queued prematurely before you exit blocks of code.

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

无人接听 2024-07-12 13:55:34

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

您还可以使用一个框架(即工厂),其中引用作为虚拟引用给出。 如果对象很多且寿命较短(即使用后被丢弃),这非常有用。 如果你有草率的程序员认为垃圾收集很神奇,那么清理内存非常方便。

It can allow you two have phantom caches which are very efficient in memory management.
Simply put, if you have huge objects that are expensive to create but seldom used, you can use a phantom cache to reference them and be sure they do not take up memory that is more valuable. If you use regular references you have to be manually make sure there are no references left to the object. You can argue the same about any object but you dont have to manually manage the references in your phantom cache. Just have to be carefull to check if they have been collected or not.

Also you can use a framework (i.e. a factory) where references are given as phantom references. This is useful if the objects are many and short lived (i.e. used and then disposed). Very handy for clearing memory if you have sloppy programmers that think garbage collection is magical.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文