欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WeakHashMap应用实例之SNMP4j中缓存的应用

程序员文章站 2024-02-11 20:56:04
...

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中缓存的应用
 

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


WeakHashMap应用实例之SNMP4j中缓存的应用
 

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


WeakHashMap应用实例之SNMP4j中缓存的应用
 

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


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

 

总结:

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