1

私には奇妙な状況があります。

オブジェクトのグラフで equal() であるインスタンスを再利用できる単純な flyweight ファクトリがあります。

flyweight を使用して、または使用せずにルート オブジェクトをシリアル化してその利点を測定すると、参照ごとに新しいオブジェクトを含む 2,014,169 バイトから 1,680,865 バイトに減少します。大丈夫です。

しかし、jvisualvm のヒープ ダンプでこのオブジェクトの保持サイズを見ると、常に 6,807,832 と表示されます。

どうしてですか?確かに、あるケースで同じオブジェクトの複数のインスタンスがある場合、それらはそれぞれメモリを占有します。保持サイズは、GC から回収される量である必要があります。flyweight factory を使用してインスタンスをリサイクルしなければ、それ以上になると思います。シリアライゼーションのメリットが見られない場合は、フライウェイト ファクトリのバグだと思いますが、シリアライゼーションでのみ機能する方法がわかりません。

今、私は少し困惑しています。

flyweight ファクトリを使用して、新しいインスタンスをチェックに渡し、代わりに参照を再利用できるかどうかを確認します。

map.put(key, flyweightFactory.get(new MyClass()));

flyweight を使用しない場合は、毎回新しいオブジェクトを保存します。

map.put(key, new MyClass());

参考までに、flyweight ファクトリ クラスを次に示します。

/**
 * 
 * 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() + "]";
    }
}
4

1 に答える 1

0

問題は、フライウェイト ファクトリ自体をリリースしていなかったことです。ルート オブジェクトからは参照されませんが、flyweight オブジェクトへの他の参照を保持することにより、それらは保持サイズにカウントされません。

私のテストケースが修正され、ルートオブジェクト以外のフライウェイトへの参照がなかった場合、保持されるサイズは、フライウェイトを使用して最大 9.2 MB になり、フライウェイトを介して等しいインスタンスをリサイクルしないと 10.3 MB になりました。

オブジェクトの保持されたサイズにだまされました。その 6.8 MB は、コンテナー オブジェクトと参照のオーバーヘッドにすぎません (キーもフライウェイトであった場合)。フライウェイトがカウントされていないとは思いもしませんでした。

これは実際には非常に有益です。犯すのは便利な間違いでした!コンテナー自体を調べて、ハッシュマップを列挙マップに置き換えることでメリットが得られるかどうかを確認する必要があります (10 MB はそれほど多くないように思えるかもしれませんが、フットプリントを小さくすることを目指しています!)

ちなみに、フライウェイトの有無にかかわらず、GC を実行しても違いはありませんでした。

ご意見をお寄せいただきありがとうございます。

于 2016-10-08T00:36:39.567 に答える