At first, I was creating the "type handler" cfc object (using
createObject ()) every time I wanted to access a database object, but
I found that createObject is very slow. Now I create the "type
handler" object once and store it in the "type information" that is
cached in the Application scope. So the "type handler" cfc's are now
being cached after I create it.
In order to avoid having to lock my application scope for the entire
time it takes to access the database, I lock the application scope,
copy the "type handler" cfc to the caller's scope using the duplicate
() function, and then make method calls into the copied version of the
"type handler" cfc.
Is there a better way to do this?
I've got a several other instances where I'm unsure about the best
method for accessing data and cfc's that are cached in the Application
scope without having to lock everything all the time. Another example
is that I create a siteMap cfc object that contains a hierarchical
array-based structure that is accessed on just about every request. I
do things like... display links to pages within the current section of
the site. I've got a method called "getSiteMapArray ()" that returns
the site map array. I haven't been calling the Duplicate () function
on the array before returning it... does anyone know if that might be
a source of a memory leak?
--
You received this message because you are subscribed to the Google Groups "CFCDev" group.
To post to this group, send email to cfc...@googlegroups.com.
To unsubscribe from this group, send email to cfcdev+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cfcdev?hl=en.
Here's a very trimmed down example of a memory leak that I still don't
understand... (excuse syntax errors, i've never run this code)... I
don't know why the JRE can't handle it, but it can't. Look at the
method: "getArray ()" If I simply return this.theArray, it causes jrun
to become extremely unstable and it will eventually fail to return any
pages, or even errors. It will simply stop responding to requests and
the jrun process seems to hang. If I return: duplicate
(this.theArray), my server stabilizes and returns back to normal...
always responding to requests.
It makes me completely rethink how I'm passing arrays around. Could
there be other leaks in my code?
<!--------------------- define the component ("theCFC")
------------------->
<cfcomponent name="theCFC">
<cfset this.theArray = ArrayNew (1)>
<cffunction name="getArray" access="public" output="no"
returnType="Array">
<cfreturn this.theArray> <!--- return using: duplicate
(this.theArray); solves the problem - JRE and server become stable
again --->
</cffunction>
<cffunction name="init" access="public" output="no"
returntype="theCFC">
<!--- generate an array of structs with nested arrays of structs ---
>
<cfloop from="1" to="1000" index="ix">
<cfset s1 = StructNew ()>
<cfset s1.ix = ix>
<cfset s1.theArray = ArrayNew (1)>
<cfloop from="1" to="1000" index="ixx">
<cfset s2 = StructNew ()>
<cfset s2.ix = ixx;
<cfset s2.name = "this is #ixx#">
<cfset ArrayAppend (s1.theArray, s2)>
</cfloop>
<cfset ArrayAppend (this.theArray, s1)>
</cfloop>
<cfreturn this />
</cffunction>
</cfcomponent>
<!------------------- somewhere to initialize -------------------------
>
<cflock scope="application" type="exclusive" throwontimeout="true"
timeout="30">
<cfset Application.theCFC = createObject ("component",
"theCFC").init ()>
</cflock>
<!------------------- the caller creates the memory leak
-------------------->
<cflock scope="application" type="readonly" throwontimeout="true"
timeout="30">
<cfset theArray = Application.theCFC.getArray ()>
</cflock>
<!--- use the array for whatever - somehow JRE GC loses track of local
var "theArray" --->
MEMORY LEAK
On Feb 11, 8:18 pm, Brian Kotek <brian...@gmail.com> wrote:
> As long as you var-scope any method-local variables, and the CFC doesn't
> have any instance data (data in the variable or this scope) changed during
> the method calls, you don't need to lock these.
>
> > cfcdev+un...@googlegroups.com<cfcdev%2Bunsu...@googlegroups.com>
To unsubscribe from this group, send email to cfcdev+un...@googlegroups.com.
The example code above represents a site map. I have a CFC that I
store in my application scope that builds and stores a site map
structure, along with some functions that do things with the site map,
such as search and visit, etc. I also have some legacy code in my
application that requires the use of the site map data structure
directly, which is why I created an accessor method to get it. If I
don't use the CF function: duplicate () to return the instance data, I
get a horrible memory leak that brings down my server every day. The
jrun process basically comes to a screeching halt and CF returns blank
pages. I'm running CF8. I've found out that CF8 did have some memory
leaks in shared scopes.
Here's an article that pretty much sums up my experience:
http://thinkinglemur.com/index.php/2010/02/memory-leaks-with-coldfusion-8/
It is true that arrays are passed by value (which sucks!!!). But I'm
pretty sure that structures within the arrays themselves are passed by
value, unless you do a deep copy using the duplicate method.
Pseudocode isn't really going to be sufficient in this case as your problem is pretty unique.
Oh, and not var-scoping your function-local variables creates them in the variables scope... which, depending on the function, can create some really nasty race conditions and/or memory leaks.
Doing a deep copy with duplicate() does NOT RETURN ANYTHING BY REFERENCE... it's the only way to return a structure by value because it creates an entirely new structure that mirrors the original.
Let's see... anything else?
Oh, the fact that returning a struct from a CFC function in the application scope causes your app to return blanks pages pretty much tells me all by itself that the issue is your code and not CF. This is how all the major frameworks work, from Fusebox on up to ColdBox, so that's not the problem.
J
On Feb 25, 2010, at 11:39 AM 2/25/10, redtopia wrote:
> ... It is true that arrays are passed by value (which sucks!!!). But I'm
> pretty sure that structures within the arrays themselves are passed by
> value, unless you do a deep copy using the duplicate method.
> ...
Let me ask you this:
You have a cfc that is stored in the application scope, and it stores
an array as part of its instance data. It also has a method that
changes the instance data (a refresh of the site map). And your
application also allows a developer to re-create that cfc because a
new version of that cfc might need to be applied to the application
(obviously a rare action).
Somewhere in you code, you need direct access to the instance data (in
this case to the site map data structure). Do you pass back a copy of
the instance data, or do you allow direct access to the instance data?
Here's what I do ( getSiteMap () returns a copy like this: <cfreturn
duplicate (this.siteMapArray) /> )
<cflock type="readonly" scope="Application" throwontimeout="true"
timeout="30">
<cfset siteMapArray = Application.siteMap.getSiteMap ()>
</cflock>
... then the code uses siteMapArray to do what it needs to do
So I lock the application scope (to handle a situation when a
developer would be re-creating the siteMap object), and then return a
copy of the instance data.
The code that handles when a developer would re-create the site map
is:
... in onRequestStart
<cfif (the developer wants to recreate the site map)>
<cflock type="exclusive" scope="Application" throwontimeout="true"
timeout="30">
<cfset Application.siteMap = createObject ("component",
siteMap).init ()>
</cflock>
</cfif>
What do you think?
On Feb 25, 10:47 am, Jared Rypka-Hauer <armchairde...@gmail.com>
wrote:
I also think that this will be the 3rd or 5th time someone said (or at least suggested) that pseudocode ain't gonna cut it here.
A couple inline comments:
J
On Feb 25, 2010, at 12:19 PM 2/25/10, redtopia wrote:
> Yes, I know that storing CFCs in a shared scope wasn't the problem.
> But I think my code exposed the memory leak bug in CF 8 that was fixed
> in one of the CF 8 hot fixes, though I haven't tested that theory. I
> solved the problem using the duplicate () function before I applied
> the hot fixes, so I don't really know.
If that were the case then your issues should have disappeared when you installed the hotfix. Since they didn't, I revert to my previous statement, the problem isn't CF, it's your code.
> Let me ask you this:
>
> You have a cfc that is stored in the application scope, and it stores
> an array as part of its instance data. It also has a method that
> changes the instance data (a refresh of the site map). And your
> application also allows a developer to re-create that cfc because a
> new version of that cfc might need to be applied to the application
> (obviously a rare action).
Are you hardcoding the site map into this CFC? Why? What purpose does any of this serve?
> Somewhere in you code, you need direct access to the instance data (in
> this case to the site map data structure). Do you pass back a copy of
> the instance data, or do you allow direct access to the instance data?
Neither. You code thread-safe functions on that CFC to deliver to the page whatever it needs, applying changes or receiving specific details.
> Here's what I do ( getSiteMap () returns a copy like this: <cfreturn
> duplicate (this.siteMapArray) /> )
>
> <cflock type="readonly" scope="Application" throwontimeout="true"
> timeout="30">
> <cfset siteMapArray = Application.siteMap.getSiteMap ()>
> </cflock>
>
> ... then the code uses siteMapArray to do what it needs to do
GET THE LOCKS OUT OF YOUR READS!! You do not need to lock reads to shared scopes, but you need to lock writes. Use NAMED LOCKS (again) instead of brute-force scope-wide locks.
> So I lock the application scope (to handle a situation when a
> developer would be re-creating the siteMap object), and then return a
> copy of the instance data.
>
> The code that handles when a developer would re-create the site map
> is:
>
> ... in onRequestStart
>
> <cfif (the developer wants to recreate the site map)>
> <cflock type="exclusive" scope="Application" throwontimeout="true"
> timeout="30">
> <cfset Application.siteMap = createObject ("component",
> siteMap).init ()>
> </cflock>
> </cfif>
>
>
> What do you think?
I think you have race conditions and thread-safety issues galore, and you're not taking what advice has already been given:
var-scope everything
stop locking reads from shared scopes
use named locks instead of scope locks
Read up on thread-safety and CFCs, too:
http://www.compoundtheory.com/?ID=21&action=displayPost (old, but still relevant)
http://corfield.org/blog/index.cfm/do/blog.entry/entry/CFCs_Scopes_amp_Thread_Safety (ditto)
http://www.cfhero.com/2009/11/being-thread-safe-in-coldboxcoldfusion.html (a bit simpler but much more recent)
I solved the problem *before* I applied the hot fixes, so I'm not sure
if it was CF or my code, but since the problem never happened on my
development environment, even when load tested, (CF 9), and there's a
hot fix that fixes memory leaks in cfcs in shared scopes, I figured
it's a pretty good chance that it was the CF bug. That's not important
anyway... what's important is that I figure out what "should" work,
and then follow that method.
> Are you hardcoding the site map into this CFC? Why? What purpose does any of this serve?
In my application, web site content is stored in a hierarchical data
model inside a database. I construct a site map when the application
is initialized so I can use it to do various sitemap related things,
like building the site's menus, and displaying links to other pages
within a section, or displaying the user's current location within the
web site, etc... all the standard web site navigation stuff.
Why do you think I have race/thread safety issues galore? Does it hurt
to read-lock? If it's not necessary, I would assume that the
underlying CF implementation amounts to a noop.
I realize using named locks are better, and I use them a lot, but I
lock the application scope whenever I write to it. Isn't that what I'm
supposed to do?
And I think a named lock can be disadvantageous for session scope
data. In most cases, you wouldn't want to perform a named lock when
you're writing to the session scope because that would lock any other
request from other session from being executed for read-only access
into that lock. Is that a correct assertion? Alternatively, locking
the session scope, will only lock that specific user from read access,
and not everyone else. Correct?
Thanks for the links... I'll check them out. (FYI, I've been a CF
developer for 11 yrs. and I have a CS degree, so you're not helping
someone who is completely clueless!)
On Feb 25, 11:53 am, Jared Rypka-Hauer <armchairde...@gmail.com>
wrote:
> http://www.compoundtheory.com/?ID=21&action=displayPost(old, but still relevant)http://corfield.org/blog/index.cfm/do/blog.entry/entry/CFCs_Scopes_am...(ditto)http://www.cfhero.com/2009/11/being-thread-safe-in-coldboxcoldfusion....(a bit simpler but much more recent)