使用享元模式后,visualvm 保留了相同的大小
visualvm retained size same after using flyweight pattern
我有一个奇怪的情况。
我有一个简单的享元工厂,允许我在对象图中重用 equal() 的实例。
当我序列化根对象(使用和不使用享元)以衡量它的好处时,我从 2,014,169 字节(每个引用一个新对象)减少到 1,680,865 字节。好的,没问题。
但是,当我在 jvisualvm 的堆转储中查看此对象的保留大小时,我总是看到 6,807,832。
怎么可能?当然,如果在一种情况下我有同一个对象的多个实例,它们每个都会占用内存。保留大小应该是将从 GC 中回收的数量。我认为如果不使用享元工厂来回收实例会更好。如果我没有看到序列化的好处,我会认为这是轻量级工厂中的一个错误,但我看不出它如何只对序列化有效。
现在有点迷茫
使用享元工厂,您通过检查传递新实例以查看是否可以重用引用:
map.put(key, flyweightFactory.get(new MyClass()));
如果不使用flyweight,每次都存储新对象:
map.put(key, new MyClass());
作为参考,这里是享元工厂 class:
/**
*
* 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() + "]";
}
}
所以问题是我没有发布享元工厂本身。它不是从根对象引用的,但是通过持有对享元对象的其他引用,它们不计入保留大小。
一旦我的测试用例被修复,并且除了通过根对象之外没有对享元的引用,使用享元时保留的大小增加到 9.2mb,没有通过享元回收相等实例时为 10.3mb。
我被对象的保留大小给骗了; 6.8mb 只是容器对象和引用的开销(假定键也是享元)。我没想到我的轻量级甚至没有被计算在内。
这实际上是非常有用的。这是一个有用的错误!我需要查看容器本身,看看我是否可以从用 enummaps 替换 hashmaps 中获益(10mb 可能看起来不多,但我的目标是低占用空间!)
顺便说一句,无论有无享元,执行 GC 都没有任何区别。
感谢您的意见。
我有一个奇怪的情况。
我有一个简单的享元工厂,允许我在对象图中重用 equal() 的实例。
当我序列化根对象(使用和不使用享元)以衡量它的好处时,我从 2,014,169 字节(每个引用一个新对象)减少到 1,680,865 字节。好的,没问题。
但是,当我在 jvisualvm 的堆转储中查看此对象的保留大小时,我总是看到 6,807,832。
怎么可能?当然,如果在一种情况下我有同一个对象的多个实例,它们每个都会占用内存。保留大小应该是将从 GC 中回收的数量。我认为如果不使用享元工厂来回收实例会更好。如果我没有看到序列化的好处,我会认为这是轻量级工厂中的一个错误,但我看不出它如何只对序列化有效。
现在有点迷茫
使用享元工厂,您通过检查传递新实例以查看是否可以重用引用:
map.put(key, flyweightFactory.get(new MyClass()));
如果不使用flyweight,每次都存储新对象:
map.put(key, new MyClass());
作为参考,这里是享元工厂 class:
/**
*
* 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() + "]";
}
}
所以问题是我没有发布享元工厂本身。它不是从根对象引用的,但是通过持有对享元对象的其他引用,它们不计入保留大小。
一旦我的测试用例被修复,并且除了通过根对象之外没有对享元的引用,使用享元时保留的大小增加到 9.2mb,没有通过享元回收相等实例时为 10.3mb。
我被对象的保留大小给骗了; 6.8mb 只是容器对象和引用的开销(假定键也是享元)。我没想到我的轻量级甚至没有被计算在内。
这实际上是非常有用的。这是一个有用的错误!我需要查看容器本身,看看我是否可以从用 enummaps 替换 hashmaps 中获益(10mb 可能看起来不多,但我的目标是低占用空间!)
顺便说一句,无论有无享元,执行 GC 都没有任何区别。
感谢您的意见。