Это правильный способ использования Soft References - PullRequest
0 голосов
/ 17 ноября 2010

Некоторое время назад я создал кэш с помощью Soft References, но, пытаясь устранить ошибку, меня беспокоит, что на самом деле я сделал это неправильно и удаляю объекты, когда этого не должно быть.Вот как я это сделал:

private static final Map<String, SoftReference<Buffered>> imageMap =
        new HashMap<String,SoftReference<Buffered>>();

public static synchronized Buffered addImage(String sum, final byte[] imageData)
    {
        SoftReference<Buffered> bufferedRef = imageMap.get(sum);
        Buffered buffered;
        if (bufferedRef!=null)
        {
            //There are no longer any hard refs but we need again so add back in
            if(bufferedRef.get()==null)
            {
                buffered = new Buffered(imageData, sum);
                imageMap.put(sum, new SoftReference(buffered));
            }
            else
            {
                buffered=bufferedRef.get();
            }
        }
        else
        {
            buffered = new Buffered(imageData, logDescriptor, sum);
            imageMap.put(sum, new SoftReference(buffered));
        }
        return buffered;
    }

    public static Buffered getImage(String sum)
{           
    SoftReference<Buffered> sr = imageMap.get(sum);
    if(sr!=null)
    {
        return sr.get();
    }
    return null;
}

Таким образом, идея заключается в том, что вызывающий процесс может добавлять новые буферизованные объекты, которые можно идентифицировать / искать по ключевой сумме, тогда, пока этот буферизованный объектесли он используется хотя бы одним объектом, он не будет удален с карты, но если он больше не используется какими-либо объектами, он может быть сборщиком мусора, если объем памяти будет ограничен.

Но, глядя на мой кодтеперь важно то, что на ключевую сумму поля всегда ссылаются где-то еще (что не всегда так)

РЕДАКТИРОВАТЬ: Итак, я попробовал решение Колина, но я немного озадачен, потому что putIfAbsent () некажется, не вернуть добавленную стоимость.Я изменил свой метод addImage, чтобы получить некоторую отладочную информацию

public static synchronized Buffered addImage(String sum, final byte[] imageData)
    {
        Buffered buffered = new Buffered(imageData, sum);
        Buffered buffered2 =  imageMap.get(sum );
        Buffered buffered3 =  imageMap.putIfAbsent(sum,buffered );
        Buffered buffered4 =  imageMap.get(sum );
        System.out.println("Buffered AddImage1:"+buffered);
        System.out.println("Buffered AddImage2:"+buffered2);
        System.out.println("Buffered AddImage3:"+buffered3);
        System.out.println("Buffered AddImage4:"+buffered4);                
        return buffered2;
    }

возвращает

Buffered AddImage1:com.Buffered@6ef725a6
Buffered AddImage2:null
Buffered AddImage3:null
Buffered AddImage4:com.Buffered@6ef725a6

Таким образом, он ясно показывает, что экземпляр Buffered не был для начала, успешно создан и добавлен, нообязательно должен быть возвращен putIfAbsent?

Ответы [ 2 ]

2 голосов
/ 18 ноября 2010

Я бы рекомендовал использовать Гуава MapMaker вместо того, чтобы делать это самостоятельно.

private static final ConcurrentMap<String, Buffered> imageMap = 
    new MapMaker().softValues().makeMap();

public static Buffered addImage(String sum, final byte[] imageData) {
  Buffered buffered = new Buffered(imageData, sum);
  Buffered inMap = imageMap.putIfAbsent(sum, buffered);
  return inMap != null ? inMap : buffered;
}

public static Buffered getImage(String sum) {           
  return imageMap.get(sum);
}

Так как это ConcurrentMap и использует putIfAbsent вам не нужно синхронизировать addImage, если только создание экземпляра Buffered обходится дорого.Это также обрабатывает фактическое удаление записей с карты, когда их значение является сборщиком мусора, в отличие от вашего кода.

Редактировать: Что делать, если вы вызываете getImage и получаете nullрезультат (возможно, потому что значение было собрано мусором)?Есть ли способ получить данные изображения byte[] на основе клавиши sum?Если это так, вы можете инкапсулировать процесс создания экземпляра Buffered для данного sum как Function<String, Buffered>.Это позволяет вам использовать вычислительную карту вместо обычной:

private static final ConcurrentMap<String, Buffered> imageMap = new MapMaker()
    .softValues()
    .createComputingMap(getBufferedForSumFunction());

Для этого вам может даже не понадобиться метод addImage ... если getвызывается на карте и не имеет записи для данного sum, она будет вызывать функцию, кэшировать результат и возвращать его.

1 голос
/ 18 ноября 2010

Если все, что вам нужно, это разрешить данным собирать мусор, когда на них нигде нет ссылок, используйте WeakHashMap.

Если вы хотите, чтобы ваша карта действительно могла воссоздать данные, если их больше нетдоступно, тогда вам нужно изменить getImage (), чтобы проверить, доступна ли ссылка, а если нет, воссоздать ее.

Мне кажется, что вы хотите, это первое.

Разница между мягкой ссылкой и слабой ссылкой заключается в том, что сборщик мусора использует алгоритмы, чтобы решить, следует ли возвращать объект с мягким доступом или нет, но всегда восстанавливает слабодоступный объект. (ссылка)

...