1

I'm saving some Java objects in files. I seralize them that way :

Class Timestamp.java:

public class Timestamp implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private static SimpleDateFormat staticFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", ENGLISH);
    private SimpleDateFormat instanceFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", ENGLISH);
    private long time;
}

Class ObjId.java:

public class ObjId implements java.io.Serializable {
    private final int x;
    private final int y;
    private final int z;

public ObjId(final int x, final int y, final int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

CachedObj.java :

class CachedObj implements java.io.Serializable {
        private static final long serialVersionUID = 1L;
        private final ObjId id;
        private final byte[] data;
        private final String eTag;
        private Timestamp modified;
        private Timestamp expires;
        private boolean mustRevalidate;

        public CachedObj(final ObjId ID, final byte[] data, final String eTag, final Timestamp modified, final Timestamp expires, final boolean mustRevalidate) {
            this.id= ID;
            this.data = data;
            this.eTag = eTag;
            this.modified = modified;
            this.expires = expires;
            this.mustRevalidate = mustRevalidate;
        }
}

The rest of my code :

    public void saveObjToFlash(CachedObj obj, String fileName) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream out = null;
                try {
                    out = new ObjectOutputStream(bos);
                    out.writeObject(obj);
                    out.flush();
                    bos.close();
                    out.close();
                    try (OutputStream outputStream = new FileOutputStream(fileName)) {
                        bos.writeTo(outputStream);
                    } catch (Exception e) {
                        e.getStackTrace();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

In another place in my code I deserialize that way :

public CachedObj getCachedObjFromFile(String filename) {
    try {
        File file = new File(filename);
        FileInputStream fileInput = new FileInputStream(file);
        ObjectInputStream objInput = new ObjectInputStream(fileInput);
        CachedObj obj= (CachedObj ) objInput.readObject();
        fileInput.close();
        objInput.close();
        return obj;
    } catch (IOException ex) {
        ex.printStackTrace();
    } catch (java.lang.ClassNotFoundException exx) {
        exx.printStackTrace();
    }
    return null;
}

And I have a function that calculates an CachedObj object size :

public int getObjectSize(CachedObj obj) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream out = null;
    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(obj);
        out.flush();
        out.close();
        bos.close();
        int sizeObj = bos.toByteArray().length;
        return sizeObj;
    } catch (IOException e) {
        e.printStackTrace();
        return -1;
    }
}

My question is, when I run the following code :

    public static void main(String[] args) {
        byte[] bytesArr = new byte[4];
        CachedObj obj1 = new CachedObj new ObjId(100, 100, 100), bytesArr , "etag100",
                new Timestamp(1659523700),
                new Timestamp(1659523700), false);
        int size1 = getObjectSize(obj1);
        saveObjToFlash(obj1, "file");
        CachedObj obj2 = getCachedObjFromFile("file"); // objects obj1 and obj2 seems to have same exact fields values
        int size2 = getObjectSize(obj2); // I always get : size2 = size1 - 2 !!
    }

Why are size1 and size2 different ? How can I get the size of obj2 such as I'll get the same value of size1 ?

E.F
  • 199
  • 1
  • 1
  • 10
  • Can you add `CachedObj`, and have you implemented writeObject / readObject on it? – DuncG Aug 14 '22 at 18:19
  • I'm betting the difference is caused by closing versus not closing. Nevertheless, I feel like I should mention that this doesn't actually bear any resemblance to how much space the objects take up in memory. – Louis Wasserman Aug 14 '22 at 18:47
  • Please [edit] your question to include your source code as a working [mcve], which can be compiled and tested by others. – Progman Aug 14 '22 at 19:20
  • Your edit does not include a full MCVE. Your `main()` method is missing (or a unit test entry), your imports are missing and your `ObjId` class contain compile errors. Keep in mind that we don't see the code you have in your IDE, only what you add to your question. – Progman Aug 14 '22 at 19:39
  • @DuncG I added some missing classes implementation. I didn't implemented custom writeObject / readObject. Should I ? – E.F Aug 15 '22 at 05:34
  • @LouisWasserman, I tried to add some proper closing but id didn't help (also in edit) – E.F Aug 15 '22 at 05:34
  • 2
    There is nothing anywhere that says it should be the same size. Any of the object references could refer to objects that have already been serialized, which Object Serialization will optimize away. Your question is founded on a fallacy. – user207421 Aug 15 '22 at 10:43

1 Answers1

2

This isn't a complete answer. Your issue with slight change in the serialised size is down to the SimpleDateFormat in Timestamp. Comment out the instanceFormat field, or make the field transient will make all the sizes the same again, or change your example to just serialise SimpleDateFormat instead of CachedObj:

private SimpleDateFormat instanceFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);

SimpleDateFormat isn't a good type of field to add to the serialized format of your class as it has a relatively big memory footprint (~ 48KB!). It would be better to created one instance in the application code, never serialised, and re-use it in same thread for all your Timestamp <-> String conversions performed rather than allocate new instance per Timestamp.

If you are performing a significant number of conversions re-using the same SimpleDateFormat for date to string formatting will reduce overall memory churn on large application servers.

By the way, your calls can be simplified with try-with-resources any adapted for use by and Serializable:

public static int getObjectSize(Serializable obj) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
        out.writeObject(obj);
    }
    return bos.size();
}
public static void saveObjToFlash(Serializable obj, String fileName) throws IOException {
    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) {
        out.writeObject(obj);
    }
}
public static <T extends Serializable> T getCachedObjFromFile(String filename) throws IOException, ClassNotFoundException {
    try (ObjectInputStream objInput = new ObjectInputStream(new FileInputStream(filename))) {
        return (T)objInput.readObject();
    }
}
DuncG
  • 12,137
  • 2
  • 21
  • 33