Использование MapMaker для создания кэша - PullRequest
8 голосов
/ 09 июля 2010

Я хочу использовать 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();

Записи никогда не собираются мусором, и мне не хватает памяти ошибка в моей тестовой программе. Метод finalize для записей 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 для создания карта, которую можно использовать как кеш? Почему моя тестовая программа не удаляется записи как можно скорее, если я использую слабые клавиши? Это возможно добавить очередь ссылок на карту кэша?

Ответы [ 3 ]

8 голосов
/ 10 июля 2010

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

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

Когда вы использовали слабые клавиши, компьютерная графика сразу их очищала, и ей не пришлось ждать полной паузы. (б / с Слабые ссылки собираются агрессивно.)

По моему мнению, если вы хотите кэш с чувствительностью к памяти с целочисленными ключами, я думаю, что уместно следующее:

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

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

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

Да, и последнее замечание о целочисленных и слабых клавишах (): 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;

Удачи.

3 голосов
/ 09 июля 2010

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

0 голосов
/ 09 августа 2010

Я хотел бы обратить ваше внимание на Suppliers.memoizeWithExpirationm, Instant-Cache.

http://guava -libraries.googlecode.com / svn / trunk / javadoc / com / google / common/base/Suppliers.html#memoizeWithExpiration(com.google.common.base.Supplier, long, java.util.concurrent.TimeUnit)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...