Я хочу использовать 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 для создания карты, которую можно использовать в качестве кэша? Почему моя тестовая программа не удаляет записи как можно скорее, если я использую weakKeys? Можно ли добавить эталонную очередь на карту кеша?
softValues
точно так же, и он работал нормально, аSoftReference
очищались, когда не хватало памяти. - person finnw   schedule 17.07.2010