Подтвердить что ты не робот

HashMap со слабыми значениями

Я реализую кеш для сохраняемых объектов. Идея такова:

  • Метод 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 заполняется пустым WeakReferences.

Я проверил WeakHashMap, но там ключи слабы (и значения по-прежнему являются сильными ссылками), поэтому с longs с WeakReferences не имеют смысла.

Какое лучшее решение для решения этой проблемы? Есть ли какая-нибудь "обратная WeakHashMap" или что-то подобное?

Спасибо

4b9b3361

Ответ 1

Вы можете использовать Guava MapMaker для этого:

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

Вы можете даже включить вычислительную часть, заменив makeMap() следующим образом:

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

Поскольку то, что вы пишете, похоже на кеш, более новый, более специализированный Cache (построенный с помощью CacheBuilder) может быть еще более актуальным для вас. Он не реализует интерфейс Map напрямую, но предоставляет еще больше элементов управления, которые могут потребоваться для кеша.

Ответ 2

A WeakReference добавляется к ReferenceQueue, предоставленному на время построения, когда его ссылка собрана.

Вы можете poll ReferenceQueue всякий раз, когда вы обращаетесь к кешу, и удерживайте HashMap<WeakReference<CustomObject>,Long>, чтобы узнать, какую запись удалить, если ссылка найдена в очереди.

В качестве альтернативы, если кеш не используется часто, вы можете наблюдать за очередью в отдельном потоке.

Ответ 3

Вы пробовали android.util.LruCache (его класс SDK11, но он также в пакете совместимости как android.support.v4.util.LruCache). Он не реализует java.util.Map, но работает как карта, и вы можете определить, сколько памяти потребуется, и оно будет стирать старые (неиспользуемые кешированные объекты сами по себе).

Ответ 4

Вы можете запускать "очистку" - вносить каждый раз в то время. Возможно, если ваш размер карты превышает пороговое значение, но максимум каждые 5 минут... что-то в этом роде.

Удерживайте циклы очистки короткими, чтобы не блокировать основные функции.

Ответ 6

Я думаю, что лучший вариант (если зависимость от Guava нежелательна) заключается в использовании пользовательского подкласса WeakReference, который запоминает его идентификатор, так что ваш поток очистки может удалить слабые значения во время очистки 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();
        }
    }
}