我正在使用MongoDB和ConcurrentHashMap java类实现缓存服务器。当有可用空间将对象放入内存时,它将放入。否则,该对象将保存在mongodb数据库中。是否允许用户为内存缓存指定内存中的大小限制(这显然不应超过堆大小限制!)。客户端可以使用通过RMI连接的缓存服务。我需要知道每个对象的大小,以验证是否可以将新的传入对象放入内存。我通过互联网搜索,我得到了这个解决方案来获得大小:
public long getObjectSize(Object o){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(o);
oos.close();
return bos.size();
} catch (Exception e) {
return Long.MAX_VALUE;
}
}
此解决方案非常有效。但是,在内存使用方面并没有解决我的问题。 :(如果许多客户端同时验证对象大小,这将导致堆栈溢出,对吧?嗯......有些人可以说:为什么你没有得到特定的对象大小并将其存储在内存中,当另一个对象是需要在内存中检查对象大小?这是不可能的,因为对象的大小可变。:(
有人可以帮帮我吗?我正在考虑从RMI通信获取套接字,但我不知道如何做到这一点......答案 0 :(得分:2)
您可以使用以下自定义FilterOutputStream解决限制序列化对象大小的问题:
write
方法调用和IOException
子类。然后将此过滤器放在ByteArrayOutputStream
和ObjectOutputStream
。
这就是代码的样子(未经过测试!):
public LimitExceededException extends IOException { ... }
public class LimitingOutputStream extends FilterOutputStream {
private int limit;
private int count;
public LimitingOutputStream(OutputStream out, int limit) {
super(out);
this.limit = limit;
}
@Override
public void write(byte b) throws IOException {
if (count++ > limit) {
throw LimitExceededException(...);
}
super.write(b);
}
@Override
// (This override is not strictly necessary, but it makes it faster)
public void write(byte[] bytes, int from, int size) throws IOException {
if (count += size > limit) {
throw LimitExceededException(...);
}
super.write(bytes, from, size);
}
}
/**
* Return the serialization of `o` in a byte array, provided that it is
* less than `limit` bytes. If it is too big, return `null`.
*/
public byte[] serializeWithLimit(Object o, int limit) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
LimitingOutputStream los = new LimitingOutputStream(bos, limit);
ObjectOutputStream oos = new ObjectOutputStream(los);
oos.writeObject(o);
oos.close();
return bos.toByteArray();
} catch (LimitExceededException e) {
return null;
}
}
是的,当超过限制时,这会使用例外“离开”,但这是IMO 良好使用例外。我挑战任何不同意这一点的人,想出更好的解决方案。把它放在另一个答案中。
顺便说一下,这真是一个很糟糕的代码:
} catch (Exception e) {
return Long.MAX_VALUE;
}
除了你可能会被抛出的IOException
之外,你还会捕获各种未经检查的异常,其中大多数都是由bug引起的...你需要知道的:< / p>
除非您尝试进行最后一次诊断,否则抓住Exception
是不好的做法。
每当捕获到意外异常时,请务必记录它们以便记录堆栈跟踪(取决于记录器配置)。或者,如果您的应用程序不使用日志记录框架,请让它调用e.printStackTrace()
。
(如果您不想在生产代码中执行此操作,请不要将其放入StackOverflow问题中......要么某些复制粘贴编码器可能只是复制它。)
答案 1 :(得分:1)
这是我对在不使用异常的情况下实现所请求功能的挑战的回应。这是@StephenC之前回答的一个相对较小的重复,除了这个实际编译和工作。
import java.io.*;
public class LimitingOutputStream extends FilterOutputStream {
private int limit;
private int count;
private boolean limitExceeded = false;
public LimitingOutputStream(OutputStream out, int limit) {
super(out);
this.limit = limit;
}
public boolean isLimitExceeded() {
return limitExceeded;
}
@Override
public void write(int b) throws IOException {
if(!limitExceeded) {
if(count++ > limit) {
limitExceeded = true;
} else {
super.write(b);
}
}
}
@Override
// (This override is not strictly necessary, but it makes it faster)
public void write(byte[] bytes, int from, int size) throws IOException {
if(!limitExceeded) {
if ( (count += size) > limit) {
limitExceeded = true;
} else {
super.write(bytes, from, size);
}
}
}
/**
* Return the serialization of `o` in a byte array, provided that it is
* less than `limit` bytes. If it is too big, return `null`.
*/
public static byte[] serializeWithLimit(Object o, int limit) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
LimitingOutputStream los = new LimitingOutputStream(bos, limit);
ObjectOutputStream oos = new ObjectOutputStream(los);
oos.writeObject(o);
oos.close();
return los.isLimitExceeded() ? null : bos.toByteArray();
}
}