Title:
Memory Leak- Persisted closures hold onto tag instances every time they execute
| View in TrackerStatus/Resolution/Reason: To Fix//BugVerified
Reporter/Name(from Bugbase): Bradley W. / ()
Created: 07/19/2019
Components: Language, Performance
Versions: 2018
Failure Type: Memory Leak
Found In Build/Fixed In Build: 2018 /
Priority/Frequency: Normal / Unknown
Locale/System: / Platforms All
Vote Count: 15
This issue has been tested in CF 11, 2016, and 2018 (latest updates) and it affects all three in the same manner.
If a closure is persisted in memory, either in an Application-scoped CFC, or just directly in the application scope and that closure executes any tag such as cflock or cfsetting, then every time the closure executes, it will retain an instance of the tag in memory. This creates a memory leak where enough executions of the closure will eventually fill up memory with tag classes.
Steps to Reproduce:
Put this code in an index.cfm file and hit it 5 times:
<cfscript>
// Only create the closure on the first call
if( !structkeyExists( application, 'lock' ) ) {
application.lock = function(){
lock name="myLockName" type="exclusive" timeout="10" {}
};
}
// Call the closure one million times.
for (i = 1; i <= 1000000; i++) {
application.lock();
}
</cfscript>
Now take a heap dump and analyze it. There will be 5 million instances of coldfusion.tagext.lang.LockTag in memory whose GC roots go through the closure. See the attached image. The path is:
CF_ANONYMOUSECLOSURE inner class > invokePage (CFDummyComponent) > udfTags (java.util.Stack) > elementData (java.lang.Object)
These tag instances are never cleared from memory. We noticed this after adding closure usage to the FileAppender in ColdBox/LogBox. The file appender is a singleton and used a closure as a handy way to provide locking around chunks of code in a functional manner. But the more times the file appender is called and more memory gets eaten up. Here is the actual real-life use case where this bug was found:
https://github.com/ColdBox/coldbox-platform/blob/v5.4.1/system/logging/appenders/FileAppender.cfc#L97-L106
If you change the repro case above to use a UDF and not a closure, the issue does not exist! This behavior is specific to closures.
Attachments:
Comments: