1

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.

Ciaran Archer
  • 12,316
  • 9
  • 38
  • 55
  • What is the reason for not allowing the task queue to expire after it runs out of items and re-initializing it when a new item is created? – Daniel Sellers Aug 26 '10 at 14:50
  • @Daniel - Well the thread is the real issue, so I could restart the thread in some way as that is whats holding on the objects (memory). That's pretty much the same idea as using a scheduled tasks. The task starts and then runs through all available objects on the queue until it's done. Then stops and checks the queue a little while later. – Ciaran Archer Aug 27 '10 at 07:53
  • If you check for the thread's existence each time you add a task then you don't even have to use a scheduler. If it exists then you append to it and if it does not then you spawn it... removes the need for scheduling. – Daniel Sellers Aug 27 '10 at 15:40
  • Is this happening all the time, or, only under load? – Aaron Greenlee Aug 27 '10 at 16:47
  • @Aaron - this happens always, load not a factor. I thought about spawning a thread, but scheduled task is simpler in this situation. Thanks for the suggestion. – Ciaran Archer Aug 28 '10 at 17:34

4 Answers4

2

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
  • 7,865
  • 1
  • 27
  • 30
  • 1
    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. 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 Aug 26 '10 at 13:58
  • 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 Aug 26 '10 at 15:35
  • No luck with explicit GC, rewrote as a scheduled task and that sorted out the memory issues. Thanks for all the feedback. – Ciaran Archer Aug 30 '10 at 08:25
0

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.

jim collins
  • 417
  • 1
  • 8
  • 17
0

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
  • 29,538
  • 35
  • 110
  • 138
  • 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 Aug 26 '10 at 14:03
-2

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

Aaron Greenlee
  • 4,587
  • 4
  • 26
  • 37