3

我正在使用 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 通信中获取套接字,但我不知道该怎么做......

4

2 回答 2

2

您可以使用自定义 FilterOutputStream 解决限制序列化对象大小的问题:

  1. write计算方法调用写入的字节数,以及
  2. 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您可能期望被抛出的 s 之外,您还会捕获各种未经检查的异常,其中大部分是由错误引起的……您需要了解:

  1. Exception除非您尝试进行最后的诊断,否则捕捉是不好的做法。

  2. 每当您捕获意外异常时,请务必记录它们以便可以记录堆栈跟踪(取决于记录器配置)。或者,如果您的应用程序不使用日志框架,那么让它调用e.printStackTrace().

(如果你不想在生产代码中这样做,也不要在 StackOverflow 问题中这样做......'因为一些复制和粘贴编码器可能只是复制它。)

于 2012-10-27T14:22:19.693 回答
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(); 
    }

 }
于 2012-10-28T04:54:37.083 回答