How much memory does my ColdFusion variable really use? – Part III
Finally, nearly the last part (there’s one more coming…). In part II I talked about the different problems we’d run into using the instrumentation code out-of-the-box without modifying it for the special scenario of using it to size ColdFusion variables. We declared those issues solved (sic!)
– so let’s have a look at some code and how to use it. All I’ve done was to grab the example from Heinz’s JavaSpecialists newsletter and changed some of the signatures (from private to protected). I then wrote a class CFMemoryCounterAgent that simply extends MemoryCounterAgent and overrides some of the methods with modified versions to cater for ColdFusion (well, technically it’s probably overloading because I modified the signature…).
Also while I was at it – I added a second argument to sizeOf() and deepSizeOf() named ignoreFlyweights. That refers back to the last section of part II – it enables you to define if the (what we have defined as well-known) flyweights are going to be counted for the total memory usage or not.
This is the modified internalSizeOf() method, that’s being used to “filter” for the the two well-known “troublemakers” in CF as discussed in part II. When I was doing some further testing, I came across a few more scenarios that could cause the memory sizing to drift off, those have been added as well:
private static long internalSizeOf(Object obj, Stack stack, Map visited, boolean ignoreFlyweights) { if (skipObject(obj, visited, ignoreFlyweights)) { return 0; } Class clazz = obj.getClass(); if (clazz.isArray()) { addArrayElementsToStack(clazz, obj, stack); } else { // add all non-primitive fields, non-CF memory tracker objects and non-SessionContext objects to the stack while (clazz != null) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers()) && !field.getType().isPrimitive() && field.getType().getName() != "coldfusion.runtime.NeoPageContext" && field.getType().getName() != "coldfusion.runtime.CfJspPage" && field.getType().getName() != "coldfusion.monitor.memory.MemoryTrackable" && field.getType().getName() != "coldfusion.monitor.sql.QueryStat" && field.getType().getName() != "coldfusion.monitor.memory.MemoryTrackerProxy" && field.getType().getName() != "javax.servlet.ServletContext") { field.setAccessible(true); try { stack.add(field.get(obj)); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } } } clazz = clazz.getSuperclass(); } } visited.put(obj, null); return sizeOf(obj, ignoreFlyweights); }
I’ve provided a .zip file (version 0.1.1) containing the source. If you don’t want to compile it yourself, just grab the CFMemoryCounterAgent.jar and apply it to your CF instance as described in part I. The .zip also contains a memorytest.cfm that should be self-explanatory and will give you an idea how it’s supposed to be used. If you want to compile it, the build file for Ant should do the trick – please note that there is a copy task in there that copies the file into the location of my CF installation’s lib folder. You pretty much would want to change that to copy the .jar file to a different location OR remove that copy task in the first place. Also – I’ve build and tested this on CF 8.0.1 on OS X. You need Java 5 or newer to run this in the first place and YMMV with other CF versions. If you try it and get weird results, let me know.
Edit: The .jar file is compiled with Java 6 and the Ant file targets Java 6 as well. Make sure that you potentially recompile the source and change the Ant file if you try to run this on Java 5.
Please keep in mind that all results you’re getting from this are JVM-based estimations and sort-of right. Also, I would strongly discourage you to build totally messed-up code for the sake of saving 250 bytes or similar. Don’t forget that the JVM has a reasonably well-working garbage collection and the GC will collect most variables that you create on a page very quickly in the first place. The techniques shown here are more for information and to raise the behind-the-scenes awareness. Besides that, it’s just interesting to see how CF deals with the different variable types. The next (and final) post of this series will have a look at some results and try to explain those. Stay tuned.



















