记录对象序列化时的大小?

时间:2010-02-01 15:03:47

标签: java serialization

在序列化某些对象时,记录某些对象大小的最佳方法是什么?例如,一旦序列化了类型为A,B,C的对象,就记录其序列化字节的大小。我们可以通过getBytes获取整个对象图的大小,但是我们想要分析整个序列化大小的最大贡献者。

ObjectOutputStream提供了writeObjectOverride,但我们不想重写序列化过程。简单来说,我们需要知道在序列化之前遇到某个对象的时间,记录当前的总字节数,然后在序列化后,取字节数的差值。似乎包含writeSerialData会起作用,但该方法是私有的。

想法?

感谢。

---更新---

以下答案/建议具有洞察力。以下是我到目前为止的情况。让我知道你的想法。感谢。

// extend to get a handle on outputstream    
MyObjectOutputStream extends ObjectOutputStream {
    private OutputStream out;

    public MyObjectOutputStream(out) {
      super(out);
      this.out = out;
    }     

    public OutputStream getOut() {
        return this.out;
    }
}


// counter
public static class CounterOutputStream extends FilterOutputStream {
    private int bytesWritten = 0;
    ...    
    public int getBytesWritten() {
        return this.bytesWritten;
    }

    public void resetCounter() {
        bytesWritten = 0;
    }

    private void update(int len) {
        bytesWritten += len;
    }
}

// go serialize    
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new MyObjectOutputStream(new CounterOutputStream(out, 1024));


// record serialized size of this class; do this for every interested class
public class MyInterestingObject {
...
  private void writeObject(ObjectOutputStream out) throws IOException {
      CounterOutputStream counter = null;
      if (out instanceof MyObjectOutputStream) {
          counter = (CounterOutputStream)((MyObjectOutputStream)out).getOut();
          counter.resetCounter();
      }

      // continue w/ standard serialization of this object
      out.defaultWriteObject();

      if (counter != null) {
          logger.info(this.getClass() + " bytes written: " + counter.getBytesWritten());    
         // TODO: store in context or somewhere to be aggregated post-serialization
      }
  }
}

2 个答案:

答案 0 :(得分:2)

最简单的解决方案是将您正在使用的OutputStream包装一个将计算字节数的实现。

import java.io.IOException;
import java.io.OutputStream;

public class CountingOutputStream extends OutputStream {
    private int count;
    private OutputStream out;

    public CountingOutputStream(OutputStream out) {
        this.out = out;
    }

    public void write(byte[] b) throws IOException {
        out.write(b);
        count += b.length;
    }

    public void write(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
        count += len; 
    }

    public void flush() throws IOException {
        out.flush();    
    }

    public void close() throws IOException {
        out.close();
    }

    public void write(int b) throws IOException {
        out.write(b);
        count++;
    }

    public int getBytesWritten() {
        return count;
    }
}

然后你就可以使用

CountingOutputStream s = new CountingOutputStream(out);
ObjectOutputStream o = new ObjectOutputStream(s);
o.write(new Object());
o.close();
// s.getBytesWritten()

答案 1 :(得分:0)

您可以在需要捕获此类数据的任何对象上实现Externalizable而不是Serializable。然后,您可以在writeExternal方法中实现逐字节字节计数,也可以通过切换到实用程序类。像

这样的东西
public void writeExternal(ObjectOutput out) throws IOException
{
    super.writeExternal(out);
    out.writeUTF(this.myString == null ? "" : this.myString);
    ByteCounter.getInstance().log("MyClass", "myString", this.myString);
}

另一种骇人听闻的方法是坚持使用Serializable,但是使用readResolve或writeReplace挂钩来捕获你需要的任何数据,例如。

public class Test implements Serializable
{
    private String s;

    public Test(String s)
    {
        this.s = s;
    }

    private Object readResolve()
    {
        System.err.format("%s,%s,%s,%d\n", "readResolve", "Test", "s", s.length());
        return this;
    }

    private Object writeReplace()
    {
        System.err.format("%s,%s,%s,%d\n", "writeReplace", "Test", "s", s.length());
        return this;
    }

    public static void main(String[] args) throws Exception
    {
        File tmp = File.createTempFile("foo", "tmp");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(tmp));
        Test test = new Test("hello world");
        out.writeObject(test);
        out.close();
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(tmp));
        test = (Test)in.readObject();
        in.close();
    }
}