Очистка кеша экземпляра - PullRequest
1 голос
/ 06 ноября 2019

У меня есть объект / класс ClassA, экземпляры которого необходимо кэшировать (назовем его cache1) с тысячами ключей. Но на самом деле количество экземпляров ClassA ограничено сотнями.

Поэтому я хочу сократить использование кеша кешем, повторно используя экземпляры ClassA.

Простой способ сделать это - использоватьпростой пул (возможно, это не общий пул):

final class ClassA {

    private byte[] bytes;
    private static final ConcurrentHashMap<String, ClassA> instancePool = new ConcurrentHashMap<>();

    private ClassA(String bigString) {
        bytes = bigString.getBytes();
    }

    public static ClassA getInstance(String key) {
        instancePool.putIfAbsent(key, new ClassA(key));
        return instancePool.get(key);
    }

    public byte[] getBytes() {
        return bytes;
    }
}

Это работает. Но проблема в том, что случаи могут меняться, даже не часто. Срок действия кэша может истечь, но не instancePool. Так что он будет содержать все больше и больше бесполезных экземпляров, что для меня выглядит как бомба замедленного действия.

Поэтому я хочу удалить из пула бесполезные (не ref by cache1) экземпляры. Простая идея - использовать другой кеш (cache2), а не instancePool. И дайте ему гораздо больше времени истечения. Но, похоже, это не идеальный способ.

Кажется, это общая проблема. Любая библиотека для этого?

Ответы [ 2 ]

1 голос
/ 06 ноября 2019

Прежде всего, я хотел бы указать на проблемы в реализации:

  1. String является неизменным, поэтому byte[] bytes может быть окончательным и заключаться в ClassA
  2. getBytes() тормозит принцип инкапсуляции ООП. Он должен получить копию byte[] bytes.
  3. instancePool.putIfAbsent(key, new ClassA(key));, каждый раз вы создаете здесь новый экземпляр ClassA, даже если он существует и не будет помещен в кэш.
  4. ClassA getInstance(String key) в случае key это большой String, тогда почему вы создаете внутреннюю копию byte[]? почему бы просто не использовать String и использовать key.charAt()?
  5. bytes = bigString.getBytes(); использует Charset cs = Charset.defaultCharset();, чтобы вы могли получить сюрприз, если key не содержит символов ASCII.

Нецелесообразно извлекать саму byte[] bytes, а также нецелесообразно делать копию каждый раз, когда вызывается getBytes(). Поэтому я рекомендую вам использовать неизменный объект. В этом случае у вас не возникнет проблем с истечением срока действия элементов, потому что каждый раз, когда их нужно менять, вы удаляете этот элемент из кэша.

Я думаю что-то. примерно так:

public final class ClassA {

    private static final Map<UUID, ClassA> POOL = new HashMap<>();

    private final UUID id;
    private final byte[] data;

    public static synchronized ClassA getInstance(UUID id, byte[] data) {
        if (POOL.containsKey(id))
            return POOL.get(id);

        ClassA obj = new ClassA(id, data);
        POOL.put(id, obj);
        return obj;
    }

    private ClassA(UUID id, byte[] data) {
        this.id = id;
        this.data = Arrays.copyOf(data, data.length);
    }

    public int getSize() {
        return data.length;
    }

    public byte getByteAt(int pos) {
        return data[pos];
    }

}
1 голос
/ 06 ноября 2019

Если вы хотите, чтобы объект истек в зависимости от времени, вы можете использовать ExpiringMap от jodah.

Как:

Map<String, Integer> map = ExpiringMap.builder().expiration(30, TimeUnit.SECONDS).build();
...