Scope Usage and Application Scaling
Mike Brunt has posted an excellent article called Good Practices For Scaling ColdFusion Applications, in which he speaks about the importance of memory management within ColdFusion applications, and calls out certain avenues of scope usage that every CF developer should pay attention too.
Great post Mike! Another 'scope' issue that I've often seen (and abused myself) is the misuse of the VARIABLES scope. Back in the early days of CF, every example you saw showed an unscoped variable, placing it into the VARIABLES scope by default. This was fine, back then, as your view layers primarily consisted of Custom Tags. Use of the VARIABLES scope, within a custom tag, was permissable as those variables were then only available within that tag (or within a nested tag, if the nested tag used the CALLER scope). Once execution of the tag was completed, the memory space of those variables was cleared.
But, since custom tags were made from .cfm templates, there is no constraint within CFML from placing VARIABLES scoped variables within any .cfm template. This, in itself, is bad, as the VARIABLES scope (in the case of a base template, not a custom tag) is not properly cleared from memory space.
What I find people doing (and I've done it myself) is using the VARIABLES scope in place of the REQUEST scope. Memory allocated to REQUEST variables is properly recovered at the end of a request. Although variables in the VARIABLES scope are no longer available (except within a persistent CFC), their memory isn't properly released at the end of request execution. (For more information on this, see Jason Sheedy's post on the ColdFusion Memory Leak, and the accompanying comment thread and other referenced posts.)
A technique that some used, to get around this issue, was to have a StructClear(VARIABLES) statement as the last line of their onRequestEnd.cfm. This would explicitly clear out the VARIABLES scope at the end of a request. But, with the advent of Application.cfc, this no longer became an option (if you use Application.cfc.) If you tried to use that statement within the onRequestEnd() method of Application.cfc, you would only be clearing the VARIABLES scope of the CFC itself, and not the request.
That's actually a good pointer at the true underlying issue: proper scope usage. Use of the VARIABLES scope within a CFC makes variables within that scope only available directly by all methods of that CFC. Use of the VARIABLES scope within a custom tag makes variables within that scope only available within that tag (or a nested tag using the CALLER scope to access them.) Within a custom tag, you can not access variables of the calling template's VARIABLES scope, unless you use the CALLER scope to do so (which should only be in the case of a nested custom tag). You can, however, access REQUEST variables through out the request, from within a custom tag, an include file, or even within a CFC (though I wouldn't suggest doing that one.) By confining the usage of the VARIABLES scope to within custom tags and CFC's, and properly setting request level variables within the REQUEST scope, you could better manage your memory usage and application performance.
I've personally seen major differences in application performance, both through Mike's advice on persistent scope usage, as well as what I've outlined above. Unlike Jason Sheedy though, I'm not very familiar with Java profiling tools, so I'll have to leave it up to someone else to test all of this for the scientific proof. I only know that it appears my applications are running better as I migrate them to this paradigm. What are your thoughts?
*** Post Revision 080609: 2230 hrs *** More revision to my comments than the post itself, but Ray, the ColdFusion Jedi himself, corrected me. According to Ray, the VARIABLES scope wasn't added until CFMX, so my supposition on the 'intention' of the VARIABLES scope was incorrect. You really have to read the comment thread to see the fun we're having;) Hopefully we'll find an answer. Cutter


Thanks,
Rob
I think blanketly suggesting using the request scope in place of the variables scope is dubious advice, unless you're dead sure you're right on this one. And if you are right, then is there a bug raised with Adobe to get it fixed ("again")?
Cheers.
--
Adam
What I see is that the heap dump with the big query in the session scope clearly shows the query. But when I look at the heap dump from running that same query just in the variables scope, it's not in there.
I'm highly suspicious of a claim that variables in the variables scope aren't properly disposed of.
I can only go off of my own experience, which goes along with what is stated above. We have a very large shared codebase that dynamically generates over 1200 applications per server. There are hundreds of variables and objects. Each major application, when converted over, appears to lessen the load to the systems.
@marc,
I'd be very curious to see your tests and results. I'm not saying you're wrong, just that I'm curious of the test cases. There's mention at the bottom of Jason's post (one of the final comments) where someone had already replicated the issue again on 8.01. Plus, I'm not really sure it is a bug. It makes sense, when you think about it. The REQUEST scope is for variables of an individual request. Current availability of the VARIABLES scope makes sense, when takin in the context of custom tag usage. Using it at the base request page level would be possible (as custom tags are .cfm), just not good practice, and potentially damaging in heavy usage scenarios.
OK, sounds reasonable. Have you raised an issue with Adobe? I see no outstanding issues relating to this sort of memory leak. Given CF9 is in the works *right now*, it's a good time to be proactive and try to get it fixed, rather than just talk about it ;-)
If you're unaware, the bug base is now publicly accessible, and is here:
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbu...#
Raise an issue, and post the number back here. I'll vote for it, and make sure it gets onto other people's radars as well.
Cheers.
--
Adam
I'll do a bit more thorough test tomorrow.
Ultimately I do mean anything, which requires a thought shift. All of the various testing that I've seen stems around objects, though the one pass I took at Java profiling was showing me simple strings and arrays in the VARIABLES scope as well (this was over a year ago, before Mike Brunt came in to help us with some server tuning).
My experience (and we've dealt with this a lot in the past few years) is a steady increase in RAM utilization for no apparent reason. Putting the original measure in place, prior to our shift to Application.cfc, was much like Adrian's experience, whereby we saw a drastic dip in utilization. After shifting to Application.cfc, we saw steady increases again, and have been gradually refactoring (with slow gains) ever since.
@Adam
This has been reported to Adobe, as far as I can tell from Jason's and other's posts, with every version of CF since 6. Again, I'm not really sure it is a bug, so much as a developer mind shift in our development process. Understanding when these scopes should be used is a major key to application health, and this particular scenario is very gray area. We have a massive application, that is extremely high traffic, which is why it is easily apparent. Most applications I've dealt with in the past don't truly get enough hammering to experience an issue. In those instances, it really becomes an issue of best practice.
Equally, if said bug results in people suggesting to use the request scope rather than the variables scope to work around it, it should be dealt with.
You're not right with your advice that mainline-code variables should be put into the request scope rather than the variables scope. A variable should be put in the request scope when (and only when) the variable must be accessible to all the elements composing the request: custom tags, CFCs & their methods, and anything else that has its own variables space outside of the mainline code. Obviously this in itself - using request variables - should be a practice which is generally avoided, because it "breaks encapsulation"; if a tag or function needs data: pass it in, if possible. Still: it's not always possible or expedient, hence the request scope.
If a variable is only intended to be used in mainline code, it should be in the variables scope (and, naturally, if there are any bugs with doing that, they should be fixed). A variable should always be placed in the most isolated and shortest-lived scope appropriate for its use. You're basically advocating using global variables for everything, which is a notion that's kind of discouraged within the first couple of weeks of programming 101.
In regards to raising bugs with Adobe: speaking from a lot of experience, if issues get raised in the bug tracker, they get looked at: "The squeaky wheel gets the oil". Especially during the pre-release cycles. Whether or not someone at Adobe (or Macromedia) might have been aware of this at some point, they might no longer be on the team, might not realise it hasn't already been fixed, or has simply forgotten. Generally I try to encourage the person who has identified the issue to also bring it to Adobe's attention (even if it's just a reminder) by way of participating in making CF better, which is why I suggested you might want to do it. However if you don't want to for some reason, I'll get it sorted. It's going to have a lot less impact if I do it though, as I'm just going to be able to say "well this doesn't happen for me, but I know this other guy with a blog..."
--
Adam
I'm not arguing encapsulation. I'm saying that the introduction of these scopes precedes encapsulation, within the terms of ColdFusion history. It's called the REQUEST scope for a reason, and I think that's been lost to some degree.
I debate your statement that a variable intended to be used in mainline code should be in the VARIABLES scope. I believe that the intention was that the VARIABLES scope was always intended to be used within custom tags. Everything we did in the 4.x days, when I started working with CF, was done with custom tags. With the later introduction of CFCs, I believe this is one of the core reasons why the VARIABLES scope is only available with the CFC (unlike the THIS scope, which honestly would seem to be a more honest scope name for variables that are accessible within THIS object).
I am also NOT advocating using global variables for everything, by any means. I am saying that, if you are declaring a variable that is only to be used within that single request, then that variable belongs in the REQUEST scope. I'm not telling anyone to access the REQUEST scope within a custom tag, and absolutely never within a CFC, as that would break encapsulation (as you mentioned).
As far as raising the issue as a bug, I think it's still a good idea. I think it would be good for someone to verify that it is still an issue on CF 9 before filing it though (I got out of Java profiling last year. I prefer to write code;)
I strongly disagree with your statement about the Variables scope. It was certainly NOT introduced for custom tags. The variables scope was added to give us a way to treat those variables as a proper scope. This was done in MX I believe - at least 3 versions after the introduction of custom tags. It was 'clean up', much like CF9's addition of LOCAL gives us a way to 'get' the UDF local scope variables.
As for CFCs and Variables - it's a matter of access. This is available inside and out - while variables is only available inside. That's it.
That said, this issue with the VARIABLES scope (not CFC internal) does appear to be just that: an issue. We had some fairly extreme issues with memory prior to refactoring, and I was looking everywhere for advice, from refactoring code to hard core server tuning. What I found is that several people have posted on this, over several years, with testing, memory profiling, etc. I recall one post (not sure where it is, but it is out there) where Charlie Arehart even volunteered time to help someone try to track this issue, because he was sceptical as well.
And, I guess I need a real clear understanding of the usage of the REQUEST scope vs the VARIABLES scope. Not only is this working very well for me, and addresses the underlying problem (for me), but it seems the logical way of going about it. Am I totally off my rocker? Did I miss a key message in these past years?
(BTW, for those who don't know me, I'm not pickin' a fight here in any way. Just trying to get some healthy discussion and settle some questions that rarely get asked, and answered even less.)
a) I'd definitely like to see some Adobe input on the memory issue, going to raise a flag.
b) In my opinion, the Request scope's main purpose was to create a variable available to all CFMs in one request. Spectra used it quite intensively as it was a custom tag heavy application. You could do request.dsn = "foo" for example, and any CFM (main file hit, included file, custom tag) could always look for it. Request variables were also popular in CF5 and earlier when App.X would lead to memory variables. But outside of that - the main reason for the scope (imho) is to share variables along one request. Variables have always been limited to the current CFM (and CFMs CFINCLUDED).
Sounds good. Makes sense. And, I can see it in relation to cfincludes (and use it extensively in this fashion).
I'm seriously wondering about some engineer input. My line of thought here, and this is fairly new thought on my part, goes something like this: Application.cfc has an onApplicationEnd() method that cleans up the APPLICATION scope, an onSessionEnd() that cleans the SESSION, and an onRequestEnd() that cleans up the REQUEST scope. Where do VARIABLES fall in this scenario?
If VARIABLES are defined within a CFC, it stands to reason that they are cleaned up when their parent objects are removed from memory (hopefully). I would assume the same of a custom tag, but that's only because those variables are not available outside that tag (and only in theory on my part). So what happens to variables within the VARIABLES scope within a base .cfm request? And, why use the VARIABLES scope within that base .cfm request, when you know (hopefully) that the REQUEST scope will be cleared when onRequestEnd() processes?
Where this really becomes interesting is within some of this Java profiling that's been done. What we've seen in some of those tests are complex objects, assigned to the VARIABLES scope, maintained within memory after the request is gone. If those objects were within one of the other scopes (APPLICATION, SESSION, REQUEST) they would be cleared when their corresponding methods ran in Application.cfc.
Maybe this is an issue with moving to Java, and with the introduction of CFC's? Maybe the cleanup of VARIABLES is an oversight? Or, maybe this (REQUEST for request, VARIABLES for CFC and custom tags) is how it was intended to begin with, but some engineer didn't document the thought process, things just worked anyway, and only a few of us have noticed? Maybe this stems from maintaining backwards compatability for custom tag usage?
I will put a revision in the main post, regarding the intention of the VARIABLES scope, until (if ever) we can get more definitive answers.
"And, why use the VARIABLES scope within that base .cfm request, when you know (hopefully) that the REQUEST scope will be cleared when onRequestEnd() processes?"
Again - let's be sure we are talking about one thing at a time here - the memory leak or the 'proper' use of Variables. Ignoring the memory leak, you would use the Variables scope for items you want to be only available within the current CFM.
Again, I'm supposing, hoping, praying that the on-End() methods queue the server to run GC on all of that.
If the VARIABLES scope is for item you only want available within the current .cfm, then why are they also available to any template that's been cfinclude'd?
A file cfincluded is basically code that ends up being pasted into the same CFM, so it makes sense (to me!) that it shares the same variables scope.
I'll buy the 'paste in' scenario.
And, I know App.cfc is about process, and the server handles the GC. But something has to tell the server "These things are no longer in use, you may now throw them away". I guess I just assumed that our Java applications triggered that at the server level, through the completion of execution of those methods. I guess that's another engineer question;)
You stated earlier that the VARIABLES scope was introduced around the MX version. If I remember correctly (it was a long damn time ago!) werent all unscoped variables in your applications put into the VARIABLES scope all the way back as early as ColdFusion 4?
That's a fine can of worms you seem to have opened there.
I've a few comments about what's come through over night from you and Ray.
I actually think the REQUEST scope is kind of a bad thing: its sole purpose is to encourage the breaking of encapsulation, as its accessibility is global to the request. The only reason to have this is to be able to access data from within places that it's probably poor practice to do so: within elements that have their own memory space (custom tags originally, not CFCs and their methods). Its sole purpose is to break the notions of encapsulation, as a short cut.
It had a place prior to CFMX because its memory management was better than that of session, application and server scoped variables: one didn't need to worry about the issue that simultaneously reading and writing a variables within those shared scope might contaminate the entire scope, requiring all access to be locked in versions prior to CFMX.
Now that this is not a problem, and giving consideration to encapsulation (and accordingly paying attention to which scope one is putting one's variables into) is strongly encouraged, I really don't see there's much place for using the request scope. And its use really shouldn't be encouraged like you are doing here.
You do seem to slightly have the wrong end of the stick as to what the on*End methods are for, or perhaps slightly confusing the difference between deleting / clearing variables and their eventual garbage collection. All CF needs to do (and it's not the job of those event handlers) is to *delete* the variables - which at the end of a request should include the entire request scope, the request's variables scope, file scope, etc etc. Thereafter it's the remit of the JVM to recycle the RAM freed-up by these deletions *when it decides it needs to*. There is no connection between when a variable (or an entire scope of variables) being deleted and when the RAM is GCed.
It seems to me - and I have not actually experienced this, I am just going on what's been said in these few blog entries - that the deletion-flagging of CFC instances which happen to be in the mainline variables scope is not being done properly (by CF), therefore the JVM doesn't consider the variable to have been deleted, and therefore is not considered for GC. Why is it different for the request scope and the variables scope? Dunno.
Those onRequestStart and onRequestEnd event handlers are not references to the scope of the same name, they are a reference to the HTTP notion of the idea of a request, and in CF's idiom it's a period of processing from when CF receives a job from the web server, until it dispatches the result back to the web server. "Coincidentally" there is a scope with the same name, whose lifetime is for the length of that request. The event handlers and scope are named for the HTTP concept, the event handlers do not derive their name from the scope (if that makes sense).
--
Adam
For my last two posts, your captcha thing has reported I had typed the wrong string in when I had not. The second attempt each time was fine.
It is more important to ensure that objects are de-referenced cleanly and efficiently and collected than it is to maintain encapsulation. I know this is controvertial but what good is a strictly engineered "OO" application if it constantly falls over due to memory leaks.
Incidentally Brian Rhinaldi also blogged excellently about similar issues, memory leaks in CF relating to CFC's.
Object dereferencing is done by CF (or *not*, as seems to be the case under discussion here). GC is done by the JVM. Encapsulation is implemented via CFML code. They are three concepts that do not really occupy the same logical space in any way that is meaningful to this discussion. This seems to be a case of "Post hoc ergo propter hoc" (having heard that term i West Wing, I never expected to be able to use it in real life).
So... err... what are you... umm... talking about? I know you know an awful lot about this sort of thing, so I am guessing I'm missing something here.
--
Adam
Right, I see.
I guess I have a fairly bloody-minded attitude to such things (people who know me will be thinking "oh, you *don't say*"). My position is that CF should get it's act together (*) and clear these variables out properly. What should NOT happen is for developers to have to abandon good practices to accommodate it. At the very least, we developers should not be sitting down and saying "oh well, that's the way it is, we gotta code around that", we should be complaining for as long and as hard as is necessary to get it sorted.
There must be a union shop steward in my background somewhere, perhaps? ;-)
However - yes of course - whilst the status is quo, one should consider the approach under discussion here - with gritted teeth - should the circumstance call for it (and if it proves to remedy the problem).
My gripe with Steve is less about the memory leak thing, but more about this espousing using request-scoped variables "by default" in mainline code because it's what he perceives as being the correct thing to do as a matter of course. It isn't.
--
Adam
(*) there is, of course, no evidence as yet to think it's not already sorted out.
I didn't forget about my promise to dig in further. The reason I took so long to get back was that I really hoped to write all of this up with a step-by-step on how I did all this. But... I didn't get the time. Still: I spent a good deal of time poking around the heap dumps, and I did not see any difference at all in behavior related to using request vs. variables scope. I tried several different datatypes, including CFCs, and observed the same behavior with each one: variables and request scoped variables cleared out upon garbage collection.
However, this did not apply to the "definitions" of the components. To accentuate this problem, I autogenerated a CFC with 1000 functions. Then I hit a cfm page that created an instance of this component. I then hammered that cfm page with jmeter, with 20 concurrent users, over and over.... you know, get CF humming. I dumped the heap and examined it, forced garbage collection, and then dumped the heap again.
Basically, after garbage collection, there was always an instance of each of those function classes still on the heap (you know... how each function in CF is actually a class (nested inner I believe) ) .
But this applied to both request and variables scope components, too. I demonstrated this by autogenerating another 1000-function CFC whose functions all had "request_" in the name, to make it easier to see in the memory analyzer.
So... one weird thing I saw was that these function definitions didn't clear out after garbage collection. I do not know why.
the other very strange thing was that a single object was taking up 400+MB of memory: "com.adobe.internal.pdftoolkit.core.fontset.impl.PDFFontSetImpl"
I do not know what his purpose is. After repeated forced GCs, this bugger stuck around. Then, after around 10 minutes or so, he dropped off and never came back. Not sure what he was all about since none of the files I was using to test with did anything with cfpdf, cfdocument, etc.
It would be nice to get confirmation one way or the other from an Adobe engineer if there are indeed performance consequences to choosing one scope or another. I sure as hell hope not.
I think it's reasonable to expect classes to be "persisted" after GC... isn't this normal? I suppose after a full GC, maybe not.
This could raise an architectural question vis-a-vis CF... should they *need* a class per CFC method? I don't think Railo does, for example (although the resulting overhead could be the same).
The bottom line here seems to be that perhaps the originally mooted situation might indeed have been resolved already?
Mike, other than that CF tends to create *really a lot* of classes (thousands&thousands, in a moderate-sized app, in my experience), is there a problem? Or is that itself the problem?
--
Adam
This is really interesting. Thank you for taking the time to write your test. It is very odd, as we have definitely seen noticable differences when using the REQUEST scope over the VARIABLES scope.
One thing that I wonder you might try with your test. In the days preceding the use of Application.cfc, it had been suggested to place StructClear(VARIABLES) as the last line of onRequestEnd.cfm. This proved effective, until migrating to Application.cfc. Obviously this would be useless within Application.cfc, is all it would accomplish would be to clear the VARIABLES scope of the cfc itself. But, if the objects are now in the REQUEST scope (as in your test), what might the effect be of adding StructClear(REQUEST) as the last line of the onRequestEnd() method? I haven't tried this with an application yet, but it might be worth trying with your test case...
@Adam -
That is odd. The only time I've had issue with the captcha is in recognizing certain items as upper case. It' a vanilla BlogCFC install (except the layout tweeks), so not really sure. I might have to ping Ray on it.
Your comment regarding framework usage didn't necessarily surprise me, but it did spark me into some research. I've just run varScoper against most of the major frameworks, with some surprising results.
The big three: Fusebox, Mach-ii and ModelGlue (Unity and Gesture) are relatively clean, with only a few variables coming up as unscoped. On the other hand, the worst offenders that I found were ColdBox, ColdSpring, Reactor, and even Transfer. Now, to be fair, some of the positives on this are coming up on test cases found in the downloads. I've also had false positives before. But, it is still surprising to see the drastic difference.
What would you recommend as a solution to this problem? (other than "don't use frameworks + ColdSpring/Reactor/Transfer ;-) )
@Eric I think Steve pointed out some important findings. In my experience the combination of any of the three resulted in applications that had GC issues that were not resolved via Full GC's but needed a CF restart, which of course is not acceptable. I got very comfortable with FuseBox from version 1 through 4.1, because the framework did not consume the application code but structured it.
It appears that when you request a GC and the JVM isn’t under memory pressure, it won’t collect the unused objects straight away; it may take up to an hour or more before those objects disappear. I wonder if this is what Jason Sheedy was seeing in his posts. ie, the objects hanging around weren’t really “leaked”, it’s just that the GC process didn’t need to collect them because there was ample free memory available, and would the objects have been collected if he had waited a few hours?
I have however seen cases where an instance of an unscoped CFC’s methods will hang around in the heap (with an instance could of 1) long after the method has been called. The CFC itself is in the heap with an instance count of 0, but all its methods have an instance count of 1, and are in “variableScope”. This test was called 20,000 times, each time creating a new instance of the class and setting it to unscoped variable. I’m still not sure if this is a real “leak” or not as there is only 1 “instance” floating around.
The var scoping issue is interesting when it comes to frameworks such as Mach-II.
If course all variables that are within CFC’s can and should be scoped, however when it comes to views in Mach-II, you can’t’ scope any variables. Doing a struct-Clear of variables in application.cfm wouldn’t touch these of these because they are “local” to the instance of viewContext.cfc.
I’m wondering if it’s worthwhile either varing variables used in view by passing through a struct to the view which has been vared in viewContext.cfc and setting the views variables to the struct, or putting a “cleanup” method in viewContext.cfc which does a structClear(variables) and is called after the view is rendered.
I run varscoper against Transfer, and pick up whatever is un-var'd. It's part of the release checklist.
I'd be surprised you were finding much in there.
Are you sure it wasn't finding false positives? varscoper does tend to find a lot of those. Also, varScoper will claim anything that is instance.foo to be unvared, when in actuality it is being used as an instance variable.
I can't speak to the other frameworks, but if there were a huge number of unvar'd variables, there would be many complaints over thread safety issues cropping up all over the place, which isn't the case for Transfer (and I would be surprised if it was the case for the other frameworks you have mentioned).
I have a feeling you have misinterpreted the results you have found quite drastically. If you could provide some code examples, I would love to see them, particularly of Transfer.
@Mark out of interest, what are the chances of possible misscoped variables appearing in the generated objects from Transfer? They likely wouldn't be picked up by varScoper would they?
Rob
Then the performance issues mentioned by Mike are probably just related to performance overhead of object creation. I said they might be false positives, though Mach-II returns no results whatsoever from varScoper. Might be worthwhile to implicitly apply the VARIABLES scope to instance variables to avoid confusion.
I am curious about @Robert's suggestion regarding generated objects though. I know that might be much harder to track, but it's a viable concern.
It is slightly possible that there are unvar'd variables in the generated object, but given the simplicity of much of the generated code, and the fine tooth comb I tend to go over those objects, I've never found one, nor has there been any issues reported in regards to multithreading that haven't been quickly resolved.
Mark
a) Var-scoping in Mach-II
All of our builds are automated and we utilize the var-scoper tool in addition to MxUnit test via ANT for *each* nightly BER zip, betas, RCs and stable builds. A nightly cannot be published to the public unless it passed all our automated testing.
b) Var-scoper tool
Mike Schierberl's tool is great, but he does go as far as saying it's 100% accurate. For the most part, we've never seen it miss a "missing var", but is more likely show false positives. I am a committer to VarScoper and have fixed a few places where false positives were being annoy (i.e. for a while it didn't know about the thread scope when dealing with threads and it didn't know about the attributes scope for custom tags).
c) Running a GC
When a GC is requested, I do believe it is merely a "suggestion" to the JVM. I've seen instances (using VisualVM) where a GC is requested and 25MB is recovered however another 100MB is recovered 10 minutes later and this is on my development machine when nothing is going on.
d) Memory leaks
In certain circumstances, I've seen behavior where a small memory leaks occur (not related to a specific scope) in regards to CFCs and certain relationships between CFCs. The whole story is extremely convoluted and I'm exhausted because I've been dealing with it all day, but I have solved the problem on my end -- I will blog about it soon and post a link back here for people.
Thanks for weighing in.I've always been impressed with Mach-II, cutting my teeth with it way-back-when on a project for a radio station. The work that you guys do, and all of the framework and open source app developers, is entirely undervalued and understated, as most forget that you guys are basically doing those things on your own time, and (usually) on your own dime.
Ultimately, the issue that Mike Brunt, myself, and many others have seen is related to the way that ColdFusion manages applications, context roots, and objects, in relation to the JVM. I am now, and forever will be, a ColdFusion proponent, but I also concede that some of the other (unnamed) CFML engines appear to have learned from those mistakes, having a better take on their management. By publicly bringing these types of issues to light, at some point we may see the changes needed to make large, scalable application operate more effectively on our favorite platform.
But here's the million dollar question.
To the people who purportedly experienced these sizeable reductions in memory usage by switching their general vars from the variables scope to request......
Is it probable that what's REALLY happening is that you have a handful of SAME NAMED variables across different .cfms of the full request's assembly? And that because of that, once you scope them into request over variables they begin overwriting each other instead of taking up new chunks of memory depending on their context?
In other words for example,
say a typical request in your app spans 5 .cfm files, and 3 of those .cfm files all create variables named i, currentUser, department, and prefs.
By normally variable/not scoping those variables, each typical request is going to break off 12 chunks of ram for those particular variables ( cumulatively, 4 each for those 3 .cfms ).
But by request scoping them as you've done, the 2nd and 3rd .cfm that use those variables will simply be overwriting the original 4 ram chunks allocated in the 1st .cfm, instead of creating their own 4.
So, in a single typical request, you're now using only 1/3rd of the ram by INCIDENTALLY re-using the same ram chunks, which depending on what these same-named variables do ( especially if they're complex variables w/ nontrivial footprints ), may be a ton, or may be just a little. But when you scale up the number of requests and usage as you report that these apps do, those memory usage differences scale up with it, potentially introducing HUGE ram usage differences.
So if any of that is the actual case, then there is the real potential for significant gains through using the request scope instead of the variables, though your code integrity might suffer for it as elaborated by Adam.
But, that would be a real, legit performance optimization. That I would genuinely like to know about.
( PS using Chrome on Win 7 64 Pro and just had the catpcha issue Adam mentioned, this is 2nd attempt )
Someone could write a CFML optimizer that rewrites your deployment-ready CFML to use a minimum quantity of unique variables and liquifies all eligible scopes into the request scope ( moving all variables into a single request variable with the merged variables as properties that get cleared/overwritten with each new .cfm in the request, ensuring that subsequent .cfm files of the same request all reuse the same/absolute minimum quantity of ram without it encroaching on application authoring. Best of both worlds.
1... 2... 3... not it.
I can only speak for myself (though others have verified this on their own). We have a 3,000+ template enterprise application that we support here at work. I have refactored major portions of this application, sometimes as a complete rewrite of process, sometimes just 'cleaning up'. We have spent a great deal of time, over the last four years, to scope every variable being used within our system, regardless of which scope it is in. In our case, we have seen significant memory improvement by utilizing the Request scope in this way, and only using the Variables scope within CFCs and Custom Tags, as necessary.