WeakHashMap应用实例之SNMP4j中缓存的应用
WeakHashMap包路径java.util.WeakHashMap,基于HashMap实现原理同时在保存键时引入了WeakReference(弱引用),这样可以达到引用但不影响gc回收此引用实例的目的,其可以尽量避免内存泄漏的情况出现。这里的弱引用简单解释下,相对于我们平时开发中用的引用关系(即强引用),弱引用不会影响gc对垃圾实例的判断,也就是弱引用中的实例如果不存在强引用是可以被gc的,但被gc回收的时机是不确定的,其依赖gc的运行。
应用场景举例:
如果一个实例键值已经存在强引用,同时我们也需要利用此键值建立其他的映射关系,当此键值无用时(即强引用消失后,那么后来建立的映射关系也就失效了。此时我们可以将映射关系中对此键值的应用设定为弱引用。
我在进行SNMP4j协议栈测试时发现其MPv3类中的缓存类Cache就是使用了WeakHashMap实现的代码如下:
protected static class Cache {
private Map<PduHandle, StateReference> entries = new WeakHashMap<PduHandle, StateReference>(25);
/**
* Adds a <code>StateReference</code> to the cache.
* The <code>PduHandle</code> of the supplied entry will be set to
* <code>null</code> while the entry is part of the cache, because the
* cache uses a <code>WeakHashMap</code> internally which uses the
* <code>PduHandle</code> as key. When
* @param entry
* the state reference to add.
* @return
* {@link SnmpConstants#SNMP_MP_DOUBLED_MESSAGE} if the entry already
* exists and {@link SnmpConstants#SNMP_MP_OK} on success.
*/
public synchronized int addEntry(StateReference entry) {
if (logger.isDebugEnabled()) {
logger.debug("Adding cache entry: "+entry);
}
StateReference existing =
entries.get(entry.getPduHandle());
if (existing != null) {
if (existing.equals(entry)) {
if (logger.isDebugEnabled()) {
logger.debug("Doubled message: "+entry);
}
return SnmpConstants.SNMP_MP_DOUBLED_MESSAGE;
}
else if (existing.equalsExceptMsgID(entry)) {
entry.addMessageIDs(existing.getMessageIDs());
}
}
// add it
PduHandle key = entry.getPduHandle();
// because we are using a weak has map for the cache, we need to null out
// our key from the entry.
entry.setPduHandle(null);
entries.put(key, entry);
return SnmpConstants.SNMP_MP_OK;
}
/**
* Delete the cache entry with the supplied <code>PduHandle</code>.
* @param pduHandle
* a pduHandle.
* @return
* <code>true</code> if an entry has been deleted, <code>false</code>
* otherwise.
*/
public synchronized boolean deleteEntry(PduHandle pduHandle) {
StateReference e = entries.remove(pduHandle);
return (e != null);
}
/**
* Pop the cache entry with the supplied ID from the cache.
* @param msgID
* a message ID.
* @return
* a <code>CacheEntry</code> instance with the given message ID or
* <code>null</code> if such an entry cannot be found. If a cache entry
* is returned, the same is removed from the cache.
*/
public synchronized StateReference popEntry(int msgID) {
for (Iterator<PduHandle> it = entries.keySet().iterator(); it.hasNext(); ) {
PduHandle key = it.next();
StateReference e = entries.get(key);
if ((e != null) && (e.isMatchingMessageID(msgID))) {
it.remove();
e.setPduHandle(key);
if (logger.isDebugEnabled()) {
logger.debug("Removed cache entry: "+e);
}
return e;
}
}
return null;
}
}
当时我不理解,为什么这里会用弱引用做缓存,难道他不怕缓存被gc影响数据的丢失么?带着此疑问我做了如下实验:
我先令其缓存一个实例,然后我不手动调用gc看看结果,如下图:
之后我先将实例存入缓存(用eclipse打断点来实现),然后手动调用gc:
此时断点处继续执行从缓存中获取实例:
很奇怪 为什么gc没有将弱引用中的实例回收,猜测此实例肯定存在强引用:
我们看到此实例同时被HashTable引用,因HashTable在一次SNMP操作过程中会一直引用此实例,故其无法gc回收了。
总结:
这里不是在给大家讲解SNMP4j是如何进行SNMP操作的,重点在于给大家介绍弱引用的一个应用场景,上面代码中的Cache类完全可以被我们重用到自己的代码中去。但要记住一点就如其名字弱引用一样,我们用这样的缓存一定要当心gc回收。
下一篇: C# 获取 PC 序列号的方法示例