views:

48

answers:

3

Hi all

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.

A: 

You may want to look into locking your use of the Application scope.

Aaron Greenlee
We have it covered - locking is handled by the objects themselves, hasNext() takes care of it.
Ciaran Archer
The application scope has been thread-safe since CF6.
Ben Doom
I presumed the locking Aaron was speaking was to ensure queue consistency - I have the same named lock when adding and taking from the queue.
Ciaran Archer
+2  A: 

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?

Ben Doom
Hey Ben - I tried setting the object to an empty string - no joy. We own the boxes and we run scheduled tasks all the time, sometimes for less than the Adobe 1 min limit using some XML hackery on the neo-cron.xml file. For now I have added in a var keyword, i.e. <cfset var tmpItem = APPLICATION.myQueue.next() /> and also added in an explicit garbage collection call every 100 items, so if this doesn't fix it, a scheduled task might be the next thing to consider. Thanks for the feedback .
Ciaran Archer
Like I said, I really didn't know if the empty string trick really works. It looks dubious to me, but the lead tech at the company I work for says it works on CF6. Good luck with your explicit GC.
Ben Doom
No luck with explicit GC, rewrote as a scheduled task and that sorted out the memory issues. Thanks for all the feedback.
Ciaran Archer
A: 

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.

Joel
I mentioned that i'm using jmap. The creates a hprof format dump of the heap. The Memory Analysis Tool Eclipse is similar to jhat and it is showing the CF thread as holding onto all the objects. So I know exactly where the leak is! I don't think my objects are overriding finalize() (at least I'm not!) so I'm not sure that's applicable.
Ciaran Archer