#java #memory #profiling #visualvm #flyweight-pattern
#java #память #профилирование #visualvm #шаблон flyweight
Вопрос:
У меня странная ситуация.
У меня есть простая фабрика flyweight, которая позволяет мне повторно использовать экземпляры, которые равны () в графе объектов.
Когда я сериализую корневой объект с использованием и без использования flyweight, чтобы измерить его выгоду, я перехожу от 2 014 169 байт с новым объектом для каждой ссылки до 1 680 865. Хорошо, это нормально.
НО, когда я смотрю на сохраненный размер этого объекта в дампе кучи в jvisualvm, я всегда вижу 6 807 832.
Как это может быть? Конечно, если в одном случае у меня есть несколько экземпляров одного и того же объекта, каждый из них занимает память. Сохраненный размер должен быть размером, который будет восстановлен из GC. Я бы подумал, что это было бы больше без использования фабрики flyweight для переработки экземпляров. Если бы я не видел преимущества в сериализации, я бы подумал, что это ошибка на фабрике flyweight, но я не вижу, как это будет работать только для сериализации.
Сейчас я немного озадачен.
Используя фабрику flyweight, вы передаете новые экземпляры через проверку, чтобы увидеть, можно ли использовать ссылку повторно:
map.put(key, flyweightFactory.get(new MyClass()));
Если не используется flyweight, сохраняйте новый объект каждый раз:
map.put(key, new MyClass());
И для справки, вот класс flyweight factory:
/**
*
* Provides simple object reuse a la the flyweight pattern. Not thread safe.
*
* @author sigmund.segfeldt
*
* @param <A> type to be stored in the flyweight factory
*/
public class FlyweightFactory<A> {
private final Map<A, A> flyweights = new HashMap<>();
private int reuses = 0;
/**
*
* returns an instance of A, which is equal to the instance provided, ensuring
* that the same reference is always supplied for any such equal objects.
*
* @param instance
* @return a reference to an equal to instance, possibly instance itself
*/
public A get(A instance) {
A flyweight;
if (flyweights.containsKey(instance)) {
flyweight = flyweights.get(instance);
reuses;
} else {
flyweights.put(instance, instance);
flyweight = instance;
}
return flyweight;
}
/**
*
* @return the size of the flyweight factory; i.e. the number of distinct objects held
*/
public int size() {
return flyweights.size();
}
/**
*
* @return number of times a flyweight has been reused, purely for statistics to see how beneficial flyweight is (without
* taking into consideration the size of reused objects, of course).
*/
public int reuses() {
return reuses;
}
@Override
public String toString() {
return "FlyweightFactory[size " size() ", reuses=" reuses() "]";
}
}
Комментарии:
1. добавьте скриншот visualvm. Попробуйте настроить heapspace и другое соотношение пространства, чтобы посмотреть, имеет ли это какое-либо значение для сборщика мусора.
2. Кроме того, возможно, вы не создаете хороший тестовый пример, который показал бы эффект, который вы ищете. В обоих сценариях может создаваться одинаковое количество объектов, или они удерживаются каким-либо объектом и не собирают мусор
3. Выполняли ли вы performGC() в jvisualvm перед heapDump()?
4. Да, тестовый пример был неправильным, спасибо! Даст более подробную информацию в ответе
Ответ №1:
Итак, проблема заключалась в том, что я не выпустил саму фабрику flyweight. На него не ссылаются из корневого объекта, но, удерживая другие ссылки на объекты flyweight, они не учитываются в сохраненном размере.
Как только мой тестовый пример был исправлен, и не было никаких ссылок на весовые коэффициенты, кроме как через корневой объект, сохраненный размер увеличился до 9,2 МБ с flyweight, 10,3 мб без повторного использования равных экземпляров через flyweight.
Я был обманут сохраненным размером объекта; что 6,8 мб — это просто накладные расходы на объекты контейнера, а также ссылки (учитывая, что ключи тоже были flyweight). Я не думал, что мои веса даже не учитываются.
На самом деле это очень информативно. Это была полезная ошибка! Мне нужно взглянуть на сами контейнеры, посмотреть, смогу ли я извлечь выгоду из замены hashmaps на enummaps (10 мб может показаться небольшим, но я стремлюсь к малому размеру!)
Кстати, выполнение GC не имело никакого значения, ни с flyweight, ни без него.
Спасибо за ваш вклад, ребята.