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

Java 如何获取使用RMI发送的对象的大小(以字节为单位)?,java,stream,size,inputstream,rmi,Java,Stream,Size,Inputstream,Rmi,我正在用MongoDB和ConcurrentHashMapJava类实现一个缓存服务器。当内存中有足够的空间放置对象时,它将被放置在。否则,该对象将保存在mongodb数据库中。允许用户为内存缓存指定内存大小限制(这显然不应超过堆大小限制!)。客户端可以使用通过RMI连接的缓存服务。我需要知道每个对象的大小,以验证是否可以将新传入的对象放入内存。我在互联网上搜索,得到了这个解决方案以获得尺寸: public long getObjectSize(Object o){ try {

我正在用MongoDB和ConcurrentHashMapJava类实现一个缓存服务器。当内存中有足够的空间放置对象时,它将被放置在。否则,该对象将保存在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通信获取套接字,但我不知道如何做到这一点…

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

  • 统计
    write
    方法调用写入的字节数,以及
  • 当计数超过限制时,抛出自定义的
    IOException
    子类
  • 然后将此过滤器置于
    ByteArrayOutputStream
    ObjectOutputStream
    之间

    这就是代码的外观(未测试!):

    是的,当超过限制时,这会使用异常来“退出”,但这是一种很好的异常使用。我向任何不同意的人挑战,提出更好的解决方案。请用另一个答案回答


    顺便说一下,这是非常糟糕的代码:

    } catch (Exception e) {
        return Long.MAX_VALUE;
    }
    
    除了您可能期望抛出的
    IOException
    s之外,您还捕获了各种未检查的异常,其中大多数是由bug引起的……您需要了解:

  • 捕捉
    异常
    是一种不好的做法,除非您试图进行最后的诊断

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


  • (如果您不想在生产代码中执行此操作,也不要在StackOverflow问题中执行此操作…“因为一些复制粘贴编码器可能会直接复制它。)以下是我对在不使用异常的情况下实现所请求的功能这一挑战的回应。这是@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(); 
        }
    
     }
    

    使用未捕获的异常仅在日志记录方面是一种不好的做法,或者在性能方面存在一些影响?@greybearedgeek-“…这也意味着您可能不应该从字节计数过滤器OutputStream中抛出一个异常。”请随意提出更好的解决方案。序列化大于用户施加限制的对象是一个例外情况。@StephenC-我认为用户试图违反限制是正常的,因此应该由应用程序处理。计算字节数的方法应该这样做-计算字节数,然后返回size。一个单独的方法应该决定这是否违反了某个限制,并返回一个布尔值。应该保留一个异常,以便在真正意外的情况发生时使用。异常警察再次罢工。您开始怀疑是否有任何地方可以使用异常。@GreybeardGeek如果这很常见,OP为什么要尝试预处理发泄一下?@greybearedgeek-我在答案中加入了一个“完整”的实现。我要求你也这样做(在另一个答案中)为了证明您的主张,没有异常的解决方案更好。请记住,解决方案必须满足规定的要求,即如果序列化对象大于限制,它将不会填满堆。感谢解决方案人员。哦,太好了。与其抛出异常说我们失败,我们允许下一个级别继续写,写,写。你真的认为这比抛出一个异常要好吗?
     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(); 
        }
    
     }