Thursday 21 July 2011

Caculating the size of a graph of objects in Java

Finding out the actual size of an object in memory is not easy in Java. In C there is a trivial sizeof operator that will tell you, not so in Java. One has to go to the length of using a javaagent (see for example stackoverflow's discussion on this) or play around with Runtime#freeMemory and Runtime#gc() and hope that garbage collection is in any shape, form or way reliable.

Even harder is the question of how much memory a complete structure or graph of objects consumes. For example, how large is that big HashSet of Strings that I just created? There appears to be no built in support for that. However, with Instrumentation#getObjectSize and a bit of reflection this can be determined in a relatively small amount of code, which I have put on Gist.

Results on a Java 6 Mac OS JVM: empty HashSets take 144 bytes, putting a string like "string" in it shoots up to 256 bytes, adding the ubiquitous "hello world" gets us to 360 bytes and so forth. A rather staggering amount of memory all in all. Best not to look too deeply into this ...

Sunday 19 June 2011

Building for Java 5 on Java 6


During some recent work in Apache Aries, I had a need to ship some of the modules to a client who is using Java 5. No problem on the surface since the modulers were already compiled to 1.5 binaries, were it not for the fact that compiling to 1.5 class files includes a full zero percent guarantee that the class files actually do not use JDK API introduced in Java 6. And lo and behold there were a couple of calls to String#isEmpty and the IOException constructors that take an Exception argument - plus ubiquitous use of @Override on methods inherited from interfaces (a feature allowed by the Java 6 compiler but not a Java 5 compiler).



Ordinarily, the solution in this kind of situation - after fixing all the JDK method calls and annotation usages - would be to build on Java 5. However, there are unfortunately modules in Apache Aries that genuinely require a Java 6 compiler and runtime. So this was not an option. Leaving things as they are on the other hand would mean that Java 5 compatibility would likely rot very quickly as JDK 6 calls get re-introduced.



Enter Animal Sniffer, a very nifty Maven plugin that performs an API usage check on compiled binaries against a library signature. With just a bit of Maven plugin setup as shown below this plugin can enforce usage of the Java 5 class library in the projects that are meant to be Java 5 compatible. Even better since we continue to compile on Java 6, we can also continue to use @Override in the Java 6 way since it's a compile time annotation. Lastly, Animal Sniffer is actually very fast, so the overhead for doing the check is minimal.