我有一个对象/类ClassA,它的实例需要使用成千上万个键进行缓存(我们称其为cache1)。 但是实际上,ClassA的实例数限制为数百。
所以我想通过重用ClassA实例来压缩缓存的堆使用情况。
一个简单的方法是使用一个简单的池(也许这不是一个普通池):
final class ClassA {
private byte[] bytes;
private static final ConcurrentHashMap<String, ClassA> instancePool = new ConcurrentHashMap<>();
private ClassA(String bigString) {
bytes = bigString.getBytes();
}
public static ClassA getInstance(String key) {
instancePool.putIfAbsent(key, new ClassA(key));
return instancePool.get(key);
}
public byte[] getBytes() {
return bytes;
}
}
有效。但是问题在于实例可以更改,甚至不经常更改。
缓存可以过期,但instancePool
不能过期。因此它将容纳越来越多的无用实例,对我而言,这就像一颗定时炸弹。
所以我想从池中清除无用的实例(不由cache1引用)。 一个简单的想法是使用另一个cache(cache2)而不是instancePool。并给它更长的到期时间。但这似乎不是理想的方法。
这似乎是一个常见问题。有库可以这样做吗?
答案 0 :(得分:1)
如果要根据时间使对象过期,可以使用jodah中的ExpiringMap。
赞:
Map<String, Integer> map = ExpiringMap.builder().expiration(30, TimeUnit.SECONDS).build();
答案 1 :(得分:1)
首先,我想指出实施中的问题:
String
是不可变的,因此byte[] bytes
可以是最终的,并封装在ClassA
getBytes()
破坏了OOP封装原理。它应该检索byte[] bytes
的副本。instancePool.putIfAbsent(key, new ClassA(key));
每次都在此处创建ClassA
的新实例,即使该实例存在并且不会放入缓存中。ClassA getInstance(String key)
如果key
是大String
,那么为什么要创建内部byte[]
的副本?为什么不只使用String
本身并使用key.charAt()
?bytes = bigString.getBytes();
使用Charset cs = Charset.defaultCharset();
,因此如果key
不包含ASCII字符,您可能会感到惊讶。每次调用byte[] bytes
时都不能自动检索getBytes()
,也不能复制它。因此,建议您使用不可变对象。在这种情况下,项目的到期不会有问题,因为每次应更改时,都将从缓存中删除该项目。
我在想什么。大概是这样的:
public final class ClassA {
private static final Map<UUID, ClassA> POOL = new HashMap<>();
private final UUID id;
private final byte[] data;
public static synchronized ClassA getInstance(UUID id, byte[] data) {
if (POOL.containsKey(id))
return POOL.get(id);
ClassA obj = new ClassA(id, data);
POOL.put(id, obj);
return obj;
}
private ClassA(UUID id, byte[] data) {
this.id = id;
this.data = Arrays.copyOf(data, data.length);
}
public int getSize() {
return data.length;
}
public byte getByteAt(int pos) {
return data[pos];
}
}