Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

coldfusion.util.Key Memory Leak - Issue with Structure Keys?

Consistently, I am able to generate a Java Heap Space OutOfMemory exception with the following code on ColdFusion 9.01 (haven't tried earlier versions):

<cfset uuidGenerator = createObject("java", "java.util.UUID")>
<cfset transient = structNew()>
<cfloop from="1" to="100000" index="index">
    <cfset transient[uuidGenerator.randomUUID().toString()] = true>
</cfloop>

The code above uses the Java UUID class because its faster than ColdFusion's. The structure itself does not exist after the request (i.e. it's not in some persistent scope such as application).

As a test, I generate a heap dump just after initializing the server. Then I run this code several times and see the tenured generation fill through jConsole. After, I run another heap dump. Using Eclipse Memory Analysis Tool's Leak report I can see one large object rooted on coldfusion.util.Key.

I'm asking here in hopes others have hit similar problem, and if so, what they've done to work around it.

like image 568
orangepips Avatar asked May 05 '11 13:05

orangepips


2 Answers

Not an ideal solution, but until Adobe fix the memory leak internally you can get access to the private member ConcurrentHasMap on the coldfusion.util.Key object and manually clear it.

We have setup a scheduled task to execute this nightly and then do a GC immediately afterwards.

Compile this into a JAR file and put it somewhere in your ColdFusion class path.

import coldfusion.util.Key;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

class KeyEx
{
    public KeyEx()
    {
    }

    public void resetCache(Object k)
    {
        try
        {
            Field f = Key.class.getDeclaredField("keys");
            f.setAccessible(true);
            ConcurrentHashMap chm = (ConcurrentHashMap)f.get(k);
            chm.clear();
        }
        catch (Exception ex)
        {
            System.out.println("ZOMG something went epically wrong!");
        }
    }
}

And then you can simply instantiate the new KeyEx object in coldfusion and call resetCache passing in the coldfusion.util.Key singleton.

<cfset keys = createObject("java", "coldfusion.util.Key") />
<cfset x = createObject("java", "KeyEx").init() />
<cfset x.resetCache(keys) />
like image 53
anton Avatar answered Oct 21 '22 18:10

anton


These two example tests probably illustrate the problem a bit clearer:

-- MemoryLeak.cfm

<cfset transient = structNew() />
<cfset base = getTickCount() />
<cfloop from="1" to="10000" index="index">
  <cfset transient[hash("#base##index#")] = true >
</cfloop>
<cfoutput>
Done
</cfoutput>

-- NoMemoryLeak.cfm

 <cfset transient = structNew() />
 <cfloop from="1" to="10000" index="index">
 <cfset transient[hash("#index#")] = true >
 </cfloop>
 <cfoutput>
 Done
 </cfoutput>

Hit MemoryLeak.cfm on a CF 9.01+ box for 100 times and the memory leak is really obvious. Restart JVM and hit NoMemoryLeak.cfm as many times as you want, and the OldGen won't even notice it. I got up to 500,000 times before giving up.

I can't see OrangePips original bug# in the CF bug-base (looks like all old bugs went in the upgrade?), so I created a new one https://bugbase.adobe.com/index.cfm?event=bug&id=3119991 status currently confirmed & ToFix.

like image 2
Dave Avatar answered Oct 21 '22 18:10

Dave