Используя MapMaker для создания кэша

Я хочу использовать MapMaker для создания карты, что большие объекты кэшей, которые должны быть удалены из кэша, если существует недостаточно памяти. Эта небольшая демонстрационная программа, кажется, хорошо работает:

public class TestValue {
    private final int id;
    private final int[] data = new int[100000];

    public TestValue(int id) {
        this.id = id;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalized");
    }  
}  


public class Main {

    private ConcurrentMap<Integer, TestValue> cache;
    MemoryMXBean memoryBean;

    public Main() {
        cache = new MapMaker()
                .weakKeys()
                .softValues()
                .makeMap();
        memoryBean = ManagementFactory.getMemoryMXBean();
    }

    public void test() {
        int i = 0;
        while (true) {
            System.out.println("Etntries: " + cache.size() + " heap: "  
                + memoryBean.getHeapMemoryUsage() + " non-heap: "  
                + memoryBean.getNonHeapMemoryUsage());
            for (int j = 0; j < 10; j++) {
                i++;
                TestValue t = new TestValue(i);
                cache.put(i, t);
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
            }
       }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Main m = new Main();
        m.test();
    }

}

Однако, когда я делаю то же самое в своем реальном приложении, записи в основном удалены из кэша, как только они добавляются. В моем реальном приложении я также использую целые числа в качестве ключей, и кэшируемые значения являются блоками архива, считанными из диска, который содержит некоторые данные. Насколько я понимаю, слабые ссылки собраны "мусор", как только они больше не используются, таким образом, это, кажется, имеет смысл, потому что ключи являются слабыми ссылками. Если я создаю карту как это:

    data = new MapMaker()
            .softValues()
            .makeMap();

Записи никогда не собираются "мусор", и я получаю ошибку из памяти в своей тестовой программе. Завершить метод на записях TestValue никогда не называют. Если я изменяю метод тестирования для следующего:

public void test() {
    int i = 0;
    while (true) {
        for (final Entry<Integer, TestValue> entry :
            data.entrySet()) {
            if (entry.getValue() == null) {
                data.remove(entry.getKey());
            }
        }
        System.out.println("Etntries: " + data.size() + " heap: "
            + memoryBean.getHeapMemoryUsage() + " non-heap: "  
            + memoryBean.getNonHeapMemoryUsage());
        for (int j = 0; j < 10; j++) {
            i++;
            TestValue t = new TestValue(i);
            data.put(i, t);
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
        }
    }
}

записи удалены из кэша, и финализатор на объектах TestValue называют, но через некоторое время я также получаю ошибку из памяти.

Таким образом, мой вопрос: что правильный путь состоит в том, чтобы использовать MapMaker для создания карты, которая может использоваться в качестве кэша? Почему моя тестовая программа не удаляет записи как можно скорее, если я использую weakKeys? Действительно ли возможно добавить ссылочную очередь к карте кэша?

8
задан Grundlefleck 16 July 2010 в 22:08
поделиться

3 ответа

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

3
ответ дан 5 December 2019 в 15:19
поделиться

Есть много вещей, которые могут происходить, но что касается вашей тестовой программы, использующей мягкие значения: вы можете получить OutOfMemoryError, даже если у вас есть SoftReferences, которые еще не были собраны в мусор. Стоит повторить: вы можете получить OutOfMemoryError, даже если у вас есть SoftReferences, которые еще не были очищены.

SoftReferences немного странная штука, см. http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html для описания текущей механики. Вероятно, в вашем тестовом случае у GC просто не было времени на два полных GC.

Когда вы использовали weakKeys, CG очистил их сразу же, и ему не пришлось ждать полной паузы GC. (b/c WeakReferences собираются агрессивно.)

На мой взгляд, если вам нужен чувствительный к памяти кэш с Integer ключами, я бы подумал, что подходит следующее:

data = new MapMaker().softValues().makeMap();

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

Если вам нужно использовать System.gc(), чтобы избежать выхода из памяти, я бы рекомендовал перейти на карту LRU с фиксированным максимальным размером (см. javadoc java.util.LinkedHashMap для примера). Это не параллельно, но я ожидаю, что это даст вам лучшую пропускную способность в конце концов, чем просить систему выполнить сборку мусора с полной паузой кучу дополнительных раз.

О, и последнее замечание о целочисленных ключах и weakKeys(): MapMaker использует сравнение идентичности для ключей при использовании слабых или мягких ключей, и это довольно сложно сделать правильно. Свидетельство тому следующее:

Map<Integer,String> map = new MapMaker().weakKeys().makeMap();
Integer a = new Integer(1);
Integer b = new Integer(1);
Integer c = 1; //auto box
Integer d = 1; //auto box
map.put(a, "A");
map.put(b, "B");
map.put(c,"C");
map.put(d,"D");
map.size() // size is 3;

Удачи.

8
ответ дан 5 December 2019 в 15:19
поделиться

Я хотел бы обратить ваше внимание на Suppliers.memoizeWithExpirationm, мгновенный кеш.

http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Suppliers.html#memoizeWithExpiration (com.google.common.base.Supplier , длинный, java.util.concurrent.TimeUnit)

0
ответ дан 5 December 2019 в 15:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: