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看看结果,如下图:


WeakHashMap应用实例之SNMP4j中缓存的应用_第1张图片
 

之后我先将实例存入缓存(用eclipse打断点来实现),然后手动调用gc:


WeakHashMap应用实例之SNMP4j中缓存的应用_第2张图片
 

此时断点处继续执行从缓存中获取实例:


WeakHashMap应用实例之SNMP4j中缓存的应用_第3张图片
 

很奇怪 为什么gc没有将弱引用中的实例回收,猜测此实例肯定存在强引用:


WeakHashMap应用实例之SNMP4j中缓存的应用_第4张图片
 
WeakHashMap应用实例之SNMP4j中缓存的应用_第5张图片
 我们看到此实例同时被HashTable引用,因HashTable在一次SNMP操作过程中会一直引用此实例,故其无法gc回收了。

 

总结:

这里不是在给大家讲解SNMP4j是如何进行SNMP操作的,重点在于给大家介绍弱引用的一个应用场景,上面代码中的Cache类完全可以被我们重用到自己的代码中去。但要记住一点就如其名字弱引用一样,我们用这样的缓存一定要当心gc回收。

你可能感兴趣的:(java,弱引用,缓存,WeakHashMap,MPv3)