visualvm сохранил тот же размер после использования шаблона flyweight

#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, ни без него.

Спасибо за ваш вклад, ребята.