WeakHashMap应用实例之SNMP4j中缓存的应用
程序员文章站
2022-07-10 10:52:56
...
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回收。