26

我正在为持久存储的对象实现缓存。这个想法是:

  • 方法getObjectFromPersistence(long id); ///Takes about 3 seconds
  • 方法getObjectFromCache(long id) //Instantly

并有一个方法:getObject(long id)使用以下伪代码:

synchronized(this){
    CustomObject result= getObjectFromCache(id)
    if (result==null){
       result=getObjectFromPersistence(id);
       addToCache(result);
    }
    return result;
}

但我需要允许垃圾收集器收集 CustomObject。到目前为止,我一直在使用一个HashMap<Long,WeakReference<CustomObject>实现。问题是随着时间的推移 HashMap 变得充满了 empty WeakReferences

我检查了WeakHashMap但那里的键很弱(并且值仍然是强引用),所以使用 Wea​​kReferences 的 long 没有任何意义。

解决此问题的最佳解决方案是什么?是否有一些“逆 WeakHashMap”或类似的东西?

谢谢

4

8 回答 8

29

您可以为此使用番石榴: MapMaker

ConcurrentMap<Long, CustomObject> graphs = new MapMaker()
   .weakValues()
   .makeMap();

您甚至可以通过替换makeMap()以下内容来包含计算部分:

   .makeComputingMap(
       new Function<Long, CustomObject>() {
         public CustomObject apply(Long id) {
           return getObjectFromPersistence(id);
         }
       });

由于您正在编写的内容看起来很像缓存,因此更新、更专业的Cache(通过 a 构建CacheBuilder)可能与您更相关。它不Map直接实现接口,但提供了更多您可能需要的缓存控件。

您可以参考这里详细了解如何为 CacheBuilder 工作,这里是一个快速访问的示例:

LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
   .maximumSize(100)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .build(
       new CacheLoader<Integer, String>() {
           @Override
           public String load(Integer id) throws Exception {
               return "value";
           }
       }
   ); 
于 2012-11-16T09:06:37.233 回答
6

A在收集其引用时WeakReference添加到其ReferenceQueue提供的构建时间。

每当您访问缓存时,poll您都可以知道如果在队列中找到引用,则知道要删除哪个条目。ReferenceQueueHashMap<WeakReference<CustomObject>,Long>

或者,如果缓存不经常使用,您可以在单独的线程中查看队列。

于 2012-11-16T09:09:34.287 回答
4

您是否尝试过android.util.LruCache(它是一个 SDK11 类,但它也在兼容包中android.support.v4.util.LruCache)。它没有实现java.util.Map,但像 Map 一样工作,您可以定义它需要多少内存,它会刷新旧的(未使用的缓存对象本身)。

于 2012-11-16T09:10:27.363 回答
2

您还可以从 jboss-common http://docs.jboss.org/jbossas/javadoc/4.0.2/org/jboss/util/collection/WeakValueHashMap.java.html测试 WeakValueHashMap

于 2014-04-17T19:02:11.693 回答
2

你可以开始一个“清理” - 每隔一段时间线程。也许如果您的地图大小超过阈值,但最多每 5 分钟一次......类似的事情。

保持清理周期短,以免阻塞主要功能。

于 2012-11-16T08:59:03.290 回答
2

我认为最好的选择(如果不希望依赖 Guava)是使用一个自定义的 WeakReference 子类来记住它的 ID,这样你的清理线程就可以在清理 WeakReferences 期间删除弱值。

弱引用的实现,以及必要的 ReferenceQueue 和清理线程看起来像这样:

class CustomObjectAccess {

    private static final ReferenceQueue<CustomObject> releasedCustomObjects = 
                                                                  new ReferenceQueue<>();

    static {
        Thread cleanupThread = new Thread("CustomObject cleanup thread")                  
            while (true) {
                CustomObjectWeakReference freed = (CustomObjectWeakReference) 
                                CustomObjectWeakReference.releasedCustomObjects.remove();
                cache.remove(freed.id);
            }
        };
        cleanupThread.start();
    }

    private Map<CustomObjectID, CustomObjectWeakReference> cache;

    public CustomObject get(CustomObjectID id) {
        synchronized(this){
            CustomObject result= getFromCache(id);
            if (result==null) {
                result=getObjectFromPersistence(id);
                addToCache(result);
            }
        }
        return result;
    }

    private addToCache(CustomObject co) {
        cache.put(CustomObject.getID(), new CustomObjectWeakReference(co));
    }

    private getFromCache(CustomObjectID id) {
        WeakReference<CustomObject> weak = cache.get(id);
        if (weak != null) {
            return weak.get();
        }
        return null;
    }

    class CustomObjectWeakReference extends WeakReference<CustomObject> {

        private final CustomObjectID id;

        CustomObjectWeakReference(CustomObject co) {
            super(co, releasedCustomObjects);
            this.id = co.getID();
        }
    }
}
于 2015-11-13T23:13:04.213 回答
0

我需要存储标记的弱对象并计算而不是 using WeakHashMap<String, T>,我可以WeakHashMap<T, String>改为使用。

这是 Kotlin,但同样适用于 Java:

abstract class InstanceFactory<T> {
    @Volatile
    private var instances: MutableMap<T, String> = WeakHashMap<T, String>()

    protected fun getOrCreate(tag: String = SINGLETON, creator: () -> T): T =
        findByTag(tag)?.let {
            it
        } ?: synchronized(this) {
            findByTag(tag)?.let {
                it
            } ?: run {
                creator().also {
                    instances[it] = tag
                }
            }
        }

    private fun findByTag(tag: String): T? = instances.entries.find { it.value == tag }?.key

    companion object {
        const val SINGLETON = "singleton"
    }
}

这可以按如下方式使用:

class Thing(private val dependency: Dep) { ... }

class ThingFactory(private val dependency: Dep) : InstanceFactory<Thing>() {

    createInstance(tag: String): Thing = getOrCreate(tag) { Thing(dependency) }

}

简单的单例可以这样完成:

object ThingFactory {
    getInstance(dependency: Dependency): Thing = getOrCreate { Thing(dependency) }
}
于 2018-11-01T09:40:42.513 回答
0

Apache Commons Collections中有ReferenceMap,这是一个带有硬键和软值的映射实现(与 WeakHashMap 相反)。

于 2021-03-17T13:03:52.143 回答