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 ?
Advertisement
Answer
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(); } }