I have a particular thread that I kick off when by CF9 application starts. It manages a queue and takes items from the queue to process them. If there are no items to take from the queue it will sleep()
. This thread could live for weeks, processing and sleeping etc.
The problem I am having is that the items I am taking from the queue are being retained in the heap (looks like in the Old Generation) long after I am done with them.
A full garbage collection is done by the JVM about every hour (I can tell this from jConsole), and even when that runs the items I have processed are still retained in the heap. I can tell this because I do a jmap
heap dump and analyse it using the Memory Analysis Tool Eclipse plugin.
So here is my code, this is executed in Application.cfc
:
<!--- Kick off a new thread and run through the queue --->
<cfthread action="run" name="myThread">
<cfloop condition="APPLICATION.threadProcessing eq true">
<cfif APPLICATION.myQueue.hasNext()>
<!--- Get next item --->
<cfset tmpItem = APPLICATION.myQueue.next() />
<!--- Ask item to process itself --->
<cfset tmpItem.process() />
<!--- PROBLEM: these 'tmpItem' objects are never cleaned up by the garbage collector! --->
<!--- Then we sleep for an interval - this is to stop us hogging server resources --->
<cfset sleep(2000) />
<cfelse>
<!--- Nothing in the queue, so sleep for a while... --->
<cfset sleep(10000) />
</cfif>
</cfloop>
</cfthread>
Can anyone tell me if I am using the incorrect scope or something? Is there a way to force cleanup of my temporary objects? I presumed that calling an explicit garbage collection would not work as it's not being cleaned up anyway.
This only appeared to become a problem when we moved from CF8 to CF9.
Any and all help appreciated - I really would like to keep this thread approach and not run it as a scheduled task or something.
Thanks, Ciaran.
There are two pieces of advice I can offer. One is pretty iffy, but the other is pretty solid.
I've been told, though I've not tested it, that you can release objects to garbage collection early by assigning the empty string to their variable name. Essentially, you are reducing the pointer count on the object. I've only seen this done in a CF6.1 environment, so I'm really not sure if it would apply, assuming it actually works.
What I would look into, if it were me, is a scheduled task. It won't run as often (I think the minimum wait is 1 minute, IIRC) but should free up any memory used by the call when the task terminates. Basically, you just drop the outer loop and the sleep(), and call the page as a normal call. I'm not entirely sure how your queue is working, but since it's in the application scope, you should still be able to access it as long as your task is in the application tree (or includes that application file).
Based on your other comments, it sounds like this is not a shared environment. Are there other barriers to you running a scheduled task?
You may want to look into locking your use of the Application scope.
If I were trying to get to the bottom of this I would want to know what was preventing these objects from being gc's, I would use hprof and HAT to see what was holding on to the objects that was preventing them from being gc'd.
Another cause of memory gc issues, although it sounds like it doesn't apply in your case, is objects that override Object.finalize(). This is because they must be processed by the finaliser thread before they can be reclaimed, and if they are created faster than the finalizer thread can process them you may run into mem issues.
My understanding is that threads use a stack, not the Heap, so there is no "garbage collection" inside a thread. They're being retained in the heap because they're still on the stack. Anyone correct me if I'm wrong.