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 に答える