如何获取使用RMI发送的对象的大小(以字节为单位)?

时间:2012-10-27 14:10:42

标签: java stream size inputstream rmi

我正在使用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通信获取套接字,但我不知道如何做到这一点......

2 个答案:

答案 0 :(得分:2)

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

  1. 计算write方法调用和
  2. 写入的字节数
  3. 在计数超出限制时抛出自定义IOException子类。
  4. 然后将此过滤器放在ByteArrayOutputStreamObjectOutputStream

    之间

    这就是代码的样子(未经过测试!):

        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>

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

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

    3. (如果您不想在生产代码中执行此操作,请不要将其放入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(); 
    }

 }