Wednesday, August 24, 2011

CF: Decide if we got enough memory to succeed

Since the beginning of computing there has been the struggle between available resources and the number of computing tasks to run on them. When we had 16KB of RAM our code looked very compact and we were critical of any extra bytes that we stored or computing cycles we ran.
When, today, we easily reach 16GB of RAM the level of individual byte analysis does not quite happen. More likely than not, we tend to worry less about do we have enough memory to run this operation and assume (to our chagrin) that things will work themselves out,... right until they don't.

Which brings me to the problem at hand. Rather than running a process, thread, task and hoping things work, can we predictably make that decision instead?

In my case, this being ColdFusion I needed to find out whether I had a snowballs change in the Hot-Place to open an Excel file. Remember that ColdFusion uses the Apache POI library to read Microsoft Office documents. Works normally fairly transparently but the downfall here (it is documented as well, see POI docs) is that POI will grab big chunks of memory for processing any access to, say, a spreadsheet.

This, if not managed, gets us into an unconfortable situation of crashing the server with OutOfMemroy exceptions. Yep, not good.

So our solution was first determine a common estimation factor (spreadsheet size to JVM memory size), then use it to see whether we would have a chance of opening / loading this spreadsheet at all given the current memory envelope on the server.
Nice message to user if we had no chance, go ahead and process otherwise.
This eliminates unneccesary server crashes. Which is, indeed, a very good thing.

Here is the code snippet we used to determine available CF server memory:

<cffunction name="getMemory" returntype="numeric"
access="private" hint="return server unused available memory">
<cfscript>
var intMB = 1024 * 1024;
var objRuntime = createObject(
"java", "java.lang.Runtime").getRuntime();
var intUsedMem = objRuntime.totalMemory() - objRuntime.freeMemory();
var intAvailableMem = objRuntime.maxMemory() - intUsedMem;
return (intAvailableMem/intMB);
</cfscript>

</cffunction>

The value here will have to be compared against the expected value of memory use for your operation. For example, if you expect your spreadsheet to occupy 100MB memory while loaded into JVM and want to have a margin on 50MB, you can only proceed with the operation if the return of the above function is a value of 150 or greater.

Cheers,
B.

1 comment:

Brian Swartzfager said...

FYI: the code is actually cut off on the right side (viewing with FF 3.6).