Ever wonder how much memory an object takes?
The web has lots of great theoretical answers to that question.
Theories are great, and they give lots of great insights into how a Java compiler works. But what if things aren’t perfectly clean-cut? There are plenty of reasons to want proof including the following possibilities:
- Say you use the Spring Framework or some other mechanism where your objects aren’t composed until runtime
- Maybe you use a library/jar/package/swf/swc/etc, you don’t have access to the underlying code, and you’re trying to weigh its memory footprint
- Maybe your objects are composed differently in different states
- Maybe you use a Rapid Application Development language such as ColdFusion or PHP and want to see how much overhead they bring along
- Maybe you’re just a hard-core detailed person who wants to prove that the theory really matches reality
We’ve had to troubleshoot ColdFusion apps for memory usage, and we’ve found this approach to be helpful:
- Call for garbage collection (if applicable)
- Take a snapshot of how much memory is used
- Instantiate a whole bunch of copies of the class in question, and stuff them into an array
- Call for another garbage collection (if applicable)
- Take another snapshot of how much memory is used
- Subtract the first memory reading from the second, divide by the number of objects created, and you have a pretty good approximation of how much memory each object takes
As a side-note, the more objects you create in your test, the more your test will drown out background noise from other activity, and the more accurate your reading will be on each object.
After researching this method, we honed it a bit and made some useful discoveries. For example, in ColdFusion 7, each CFC instantiated takes up ~2kB. On top of that, each <cffunction></cffunction> in that cfc takes up an additional ~150 bytes. So the abc.cfc “class” (found below) with 3 empty methods takes up ~2.3kB. (disclaimer: I’ve heard but not yet personally verified that memory usage improves in CF8 and beyond.)
In our scenario, we had CFCs inheriting a data persistence layer including about 50 methods, our CFCs included about 50 more in addition to member data in the variables scope. Without fully understanding the under-the-hood functionality of ColdFusion 7, we were instantiating 30 CFCs per user at 20kB each (~half a meg) for a few thousand users. Then we wondered why we kept eating through our available memory so quickly.
After making this discovery we spent a few days re-factoring some code hoping to use resources better. We limited the inheritance of the persistence layer to where it was necessary. We re-factored the code to use ColdFusion queries rather than CFCs (queries wound up being MUCH lighter than similar CFCs). When we launched the changes our application used about 40% less memory.
As hinted earlier, this algorithm can also be used to analyze memory usage by basic Java components. One question I’ve dabbled with is how much memory java code takes. I may cover that subject in another post someday, but for now I’ll leave that as an exercise for the reader. In the meantime, here’s a ColdFusion script that implements the algorithm described above. Enjoy!
<!---this part executes in a cfm---> <cfset runtime = CreateObject("java","java.lang.Runtime").getRuntime() /> <cfset myArray = arrayNew(1) /> <cfset intNumberOfObjectsCreatedPerLoop = 10 /> <cfset intTotalNumberOfObjectsCreated = 0 /> <cfset intX = 0 /> <cfset intBytesBeforeTest = 0 /> <cfset intBytesAfterTest = 0 /> <cfset intBytesCreated = 0 /> <cfset intBytesIn1MB = 1048576 /> <cfset memoryThresholdInMB = 50/> <cfset obj = "" /> <cfset runtime.gc() /> <cfset intBytesBeforeTest = runtime.totalMemory() - runtime.freeMemory() /> <cfloop condition="intBytesCreated lt (intBytesIn1MB * memoryThresholdInMB)"> <cfset intTotalNumberOfObjectsCreated = intTotalNumberOfObjectsCreated + intNumberOfObjectsCreatedPerLoop /> <cfloop from="1" to="#intNumberOfObjectsCreatedPerLoop#" index="intX"> <cfset obj = CreateObject("component", "abc") /> <cfset arrayAppend(myArray, obj) /> </cfloop> <cfset intBytesAfterTest = runtime.totalMemory() - runtime.freeMemory() /> <cfset intBytesCreated = intBytesAfterTest - intBytesBeforeTest /> </cfloop> <cfoutput> <cfset runtime.gc() /> #intBytesCreated / 1024# kilobytes of objects created.<br> Each CFC takes approximately : #(intBytesAfterTest - intBytesBeforeTest) / intTotalNumberOfObjectsCreated / 1024#kB </cfoutput> <!--- this part resides in a separate file (abc.cfc) in the same directory --->
<cfcomponent name="abc"> <cffunction name="a" output="false"><!--- Really important code here. ---></cffunction> <cffunction name="b" output="false"><!--- Really important code here. ---></cffunction> <cffunction name="c" output="false"><!--- Really important code here. ---></cffunction> </cfcomponent>