http://coldfusion.com logo
#cfml-general
Title
# cfml-general
w

websolete

03/09/2022, 2:28 PM
is there a preferred way to destroy a variable nowadays during the request lifecycle? say i have some stuff going on in the pseudoconstructor area of application.cfc and i'm creating a variables-scoped variable named 'temp' which does a handful of things, then i don't need or want it anymore, is there a preference or advantage one way or another of doing
temp = javacast("null","")
vs
structdelete(variables,"temp")
?
b

bdw429s

03/09/2022, 2:43 PM
I would think that's generally unnecessary. Unless the var is extremely large or your request is extremely long.
Just let GC get it on a few seconds (when the request is done)
You're probably not going to cause it to be GC'd any sooner anyway.
w

websolete

03/09/2022, 2:46 PM
i think in the scenario i outlined above that it won't be GC'd any sooner is valid and accurate, but in the past i've had to javacast null variables being used in a heavily recursive/looping routine in order to avoid OOME. granted that was circa cf8, but was wondering if there was some de facto way to destroy vars that is accepted currently.
it's just that in the situation i described originally i KNOW i don't need that var after this one code block, so if i can proactively kill it rather than wait for the end of the request, great. hardly a deal breaker either way
a

aliaspooryorik

03/09/2022, 3:16 PM
Pulling it out into a separate function would make the variable var scoped to that function.
w

websolete

03/09/2022, 3:18 PM
but i think behaviorally it's the same, that is, when it exits the function it just flags those local vars for GC at the end of the request. i don't believe they're actually 'gone' once the function exits. is this not so?
a

aliaspooryorik

03/09/2022, 3:28 PM
A while ago then I had to rebuild a .cfm that had memory issues (classic example of something that worked when written 10 years ago when there wasn't much data!) and moving it into a separate functions (also used transient components as part of the refactor) solved the issue with memory. As far as I know when GC happens is up to the JVM but isn't tied to the CF request having to end first. I don't know that for sure, all I know is that when I refactored it then watching it in FusionReactor it would happily do GC and give you a saw tooth chart
w

websolete

03/09/2022, 3:30 PM
fair enough
d

David Buck

03/09/2022, 4:31 PM
It might be worth noting that, if your "temp" variable is a struct or object, then setting it to null or deleting it from the scope is probably just removing the reference and leaving the object itself intact. If you do a
StructClear(temp)
before deleting the reference, that might at least reduce the size of the object.
b

bdw429s

03/09/2022, 4:38 PM
@David Buck That would make no difference. All that matters is whether an object has a garbage collection root in the heap. removing the last GC root from a struct is no different from removing the structs references to its keys and then removing the references to the struct. Once the struct is no longer hard-referenced by any GC root, the struct and all objects it retains int he heap are eligible for collection. (So long as no OTHER variables are also holding a reference to values in the struct)
it exits the function it just flags those local vars for GC at the end of the request. i don't believe they're actually 'gone' once the function exits. is this not so?
@websolete A few things here • objects aren't proactively "flagged" for GC per se. When the collection runs, it simply scans the heap for objects which have no hard reference to a GC root object. If no references are found "holding" the object, then it's free to be removed including any 'downstream' objects it may have referenced which also have no remaining hard references to a GC root. • There is no relation to the end of an HTTP request and the running of your JVM's GC. Under moderate load, your young collections probably happen every few seconds and your old gen collections probably happen every few minutes. Whatever objects that are found with nothing "holding" on to them are subject to removal. (doesn't mean they will be) • At the end of an HTTP request, the thread (which doesn't go away, but is returned to a pool to service future requests) removes its references to the HTTPServletRequest/Response objects which contain all the data about the request. Adobe and Lucee also use Java's ThreadLocal storage to place the page context, which contains CFML scopes that are request specific. CF engines also remove the thread local storage at the end of the request. • A Thread is a GC root, so anything it references will not be collected. Once the thread removes its pointers to the servlet and page context objects, it is all fair game the next time the GC kicks in. Chances are, most request details and temporary variables will remain in memory for a few seconds to maybe a few minutes before the GC (which is inherently as lazy as possible) bothers to do anything with them. The bigger heap you have, the more lazy the GC will be.
✔️ 1
👍 1
On a related note, FusionReactor has a garbage collection graph under "Resources" > "Garbage Collection" which will show you how often each collection (young gen, old gen, etc) happens and how long they take each time. It's quite interesting to see.
👍 1
d

David Buck

03/09/2022, 5:14 PM
@bdw429s Let's say I have a temp variable with a very large primitive value (such as you might get with
fileRead()
, for example). Would setting
temp = ""
ever have any useful impact on memory usage during the life of a request?
b

bdw429s

03/09/2022, 5:17 PM
@David Buck That's not a "primitive" (not in the java sense anyway), it's just a byte array, but the answer is it depends. It can, if • the variable is very large • your request lives for a long time • you're running out of memory • GC is going to run before the request is over
If all of those are true, it could make a difference. if any of those are false, it probably doesn't matter
My rule of thumb is to not bother optimizing that unless you've seen a measurable memory issue with long running requests holding stuff in memory to the point that the heap is exhausted
1
On a note related. to fileRead(). If the file you're reading is text and you're processing it line by line, you can open the file and read it line by line with
fileReadLine()
without reading the entire thing into memory. very handy but only applies in some scenarios.
d

David Buck

03/09/2022, 5:31 PM
FileRead can return a byte array or a string. A string is a primitive value, right? My question wasn't really about that, though-- I was just trying to think of an example of something that might cause a very large variable value (large enough that you might have a reason to get rid of it before the request ends). What I'm really curious about is if GC needs to happen before the size reduction has any impact. My (admittedly uninformed) concept of GC is that it just cleans up unreferenced objects every so often, so I'm not sure how it applies to simple values like strings.
b

bdw429s

03/09/2022, 5:55 PM
a byte array or a string
A string is a byte array internally 😉
A string is a primitive value, right?
No, a string is an instance of the
java.lang.String
class. A primitive type in Java is lowercase such as
int
,
boolean
,
long
,
char
, etc https://www.baeldung.com/java-primitives (CFML has no concept of a "primitive" type per se)
if GC needs to happen before the size reduction has any impact
Yes, any objects not GC'd are still taking up space in the heap. Even if you have no references to it any longer in your code, it can still be there and that memory is not usable for a new object until the GC marks it as unused. If you take a heap dump of a running server and analyse it, there will always be a few dozen or hundred thousand "unreachable" objects on the heap at any point in time. Basically just waiting for themselves to be collected. This shouldn't necessarily be alarming though-- it's just how a GC'd language works.
concept of GC is that it just cleans up unreferenced objects every so often
Yep, that's correct. It's worth noting, when you do
Copy code
var myFile = fileRead( 'hugeFile.txt' );
myFile = "";
you actually create a NEW string object that has an empty string and re-point
myVar
to it. So now there's two objects on the heap (the file, and the empty string). But the file is referenced and eligible for collection. Setting to null would skip creating an extra string, but we're really talking about a negligible amount of memory for a single empty string object.
so I'm not sure how it applies to simple values like strings.
A java string is an instance of a class just like any other class and it's handled no differently. We call them "simple" values in CFML, but that differentiation is meaningless in Java and doesn't have any bearing on GC. @David Buck
d

David Buck

03/09/2022, 6:23 PM
@bdw429s I think my misconceptions about strings actually come from JavaScript, where (iirc) they are primitive values unless/until you call methods on them (at which point they get wrapped in a String object).
you actually create a NEW string object that has an empty string and re-point
myVar
to it.
Wow, I had no idea! Very interesting, and totally different from how I conceptualize it. Thanks for the detailed answers!
5 Views