Try/catch/finally with return of variable in try and re-assignment in finally

In reading over the try/catch/finally documentation at and testing some code, a question arises that I’d appreciate receiving clarification on. I may likely just be missing the obvious.

In the code and output in the example below, the value of variable z is assigned in the finally block just as x is, but the value of variable y logged after the function update runs does not reflect that assignment and is ‘try’ rather than ‘finally’. It appears that the finally block processes before the function returns but the value of the return is set prior to that and remains what it was at the point it occurs in the code and no longer depends on the value of z after that point.

If this is true and is consistent, and if the variables used here were local to the function update rather than global, is the finally block a reliable place to set a large return variable to null in order to avoid a memory leak? In other words, if z were very large and returned, and for some reason was not recognized for garbage collection, would setting it to null in the finally statement be a reliable method of releasing the memory after the return of it?

Thank you.

 let y, z, x = 'initiated';
 function update() {
    try {
        x = z = 'try;
        return z; }
       { console.log(e); }
     finally {
         x = z = 'finally'; 
        console.log('z in finally : ' + z ); }  // 'finally'
   } // close update()

 console.log( 'x before update : ' + x );  // 'initiated'
 y = update();
 console.log( 'x after update : ' + x );  // 'updated'
 console.log( 'y after update : ' + y );  // 'try'
 console.log( 'z after update : ' + z );  // 'finally'

Output in order is:
x before update : initiated
z in finally : finally
x after update : finally
y after update : try
z after update : finally

Yes, what you describe in your 2nd paragraph is the case. The return statement copies the then-current value of z into the return value slot of the stack (at least conceptually); later changes to z are irrelevant. Then the finally block executes, and finally the function returns for real.

Your garbage collection strategy might make sense for a conservative GC implementation, where it treated more of the stack as live than what was being actually used and so might find a stale z value until it was clobbered by the next function you called. But that’s not the case in SpiderMonkey nor, I’m pretty sure, any of the other major implementations today. When running in the interpreter, the current end of the JS stack is known, so there’s no need to scan anything beyond it for stale values. When running in the JIT, it’s the same story, just with the native stack in place of the JS stack. And the latter has to be this way; with a precise (nonconservative) GC, it’s unsafe to look at any values that are not known to be valid values.

1 Like

Thank you for responding to my question and for the explanation.

It may be unnecessary but because of the many articles concerning memory leaks caused by remaining references to large variables in shared scopes or DOM elements, I have made it a habit to set all variables declared in a function to null at the last statement, in one way or another, to help the GC algorithm determine when a variable is no longer needed. It isn’t possible to do so after a return of a variable since code after the return won’t execute. So, I wondered if setting the returned variable to null in the finally block would really break that reference, or at least make it a reference to null rather than a large amount of data.

Thanks again.