Reprinted -- about closure and variable recycling

Source: Internet
Author: User
Document directory
  • Basic test
  • Multiple variables
  • Multiple nested Functions
  • Variable with the same name declared in nested Functions
  • Test Results
  • Conclusion

The birth of this article is derived from a topic about closures in javascript recently planned. Due to the need to parse the impact of closures on garbage collection, we have made relevant tests for different javascript Engines.

In order to get the required knowledge from this article, please be clear before reading this articleClosureAndCommon garbage collection algorithmsHave a certain understanding.

Question proposal

Suppose there is the following code:

function outer() {    var largeObject = LargeObject.fromSize('100MB');    return function() {        console.log('inner');    };}var inner = outer();

In this piece of code,outerFunctions andinnerAClosure, Resulting ininnerFunction accesslargeObjectBut apparentlyinnerNot accessedlargeObjectIn the closurelargeObjectCan an object be recycled?

If more complex situations are introduced:

function outer() {    var largeObject = LargeObject.fromSize('100MB');    var anotherLargeObject = LargeObject.fromSize('100MB');    return function() {        largeObject.work();        console.log('inner');    };}var inner = outer();

The first obvious concept is:largeObjectCannot be recycledBecauseinnerYou must use it. HoweveranotherLargeObjectCan it be recycled? It will followlargeObjectAlways exist together, orlargeObjectIsolated and independently recycled?

Test Method

With this question, we have tested several existing modern javascript Engines, including:

  • JScript. dll of IE8
  • Chakra of IE9
  • Carakan of Opera 11.60
  • Chrome 16.0.912.63 comes with V8 (3.6.6.11)
  • SpiderMonkey of Firefox 9.0.1

The basic solution for testing is to use code similar to the following:

function outer() {    var largeObject = LargeObject.fromSize('100MB');    return function() {        debugger;    };}var inner = outer();

Use the Developer Tools (Developer Tools, Firebug, and Dragonfly) of various browsers to stop javascript Execution At the breakpoint, and check the function through the console or local variables.largeObjectIf the value exists, GC does not recycle the object.

For Some browsers (especially IE), there are two modes for Script execution (Execution Mode and Debugging mode, IE switches through the "Start Debugging" button in the Script Panel of the developer tool ), the breakpoint is hit only in the debugging mode, but different engine optimization schemes may exist in the debugging mode.Memory comparison. Open the resource browservar inner = outer();Execute a garbage collection task after one row (IE useswindow.CollectGarbage(); Use of Operawindow.opera.collect();) To view memory changes. If the memory is always occupied by MB, the GC does not recycle the object.

For the design of use cases, we can know from the ECMAScript standard that all variable access is performed through a LexicalEnvironment object, so the goal is to test in different LexicalEnvironment structures. From the standard, searchLexicalEnvironmentThere are several situations that can easily change the lexicalenvironment structure:

  1. Enter a function.
  2. Enter a SectionevalCode.
  3. UsewithStatement.
  4. UsecatchStatement.

Therefore, the following is a multi-use case test for these four cases.

Test process-level results basic test code
function outer() {    var largeObject = LargeObject.fromSize('100MB');    return function() {        debugger;    };}var inner = outer();inner();
Test Results
  • Jscript. dll-no recovery, no reduction in memory.
  • Chakra-reclaim, the memory will be restoredouterThe status before the function is executed.
  • Carakan-No recycle, no drop in memory.
  • V8-recycle, accesslargeObjectThrow a referenceerror.
  • Spidermonkey-recycle, accesslargeObjectGetundefined.
Conclusion

When a functionouterReturns another function.innerChakra, V8, and spidermonkeyouterButinnerWhere V8 directly unbinds the variable from lexicalenvironment, while spidermonkey only sets the value of the variableundefined, Does not unbind.

Code for multiple variables
function outer() {    var largeObject = LargeObject.fromSize('100MB');    var anotherLargeObject = LargeObject.fromSize('100MB');    return function() {        largeObject;        debugger;    };}var inner = outer();inner();
Test Results
  • Jscript. dll-no recovery, no reduction in memory.
  • Chakra-RecycleanotherLargeObject, Memory will returnouterBefore the call, it is increased by about MB.
  • Carakan-No recycle, no drop in memory.
  • V8-recycle, accesslargeObjectGet the correct value, accessanotherLargeObjectThrow a referenceerror.
  • Spidermonkey-recycle, accesslargeObjectGet the correct value, accessanotherLargeObjectGetundefined.
Conclusion

When multiple variables are bound to a lexicalenvironment, chakra, V8, and spidermonkey determine whether different variables are used. This method is used to scan the returned functions.innerThe source codeNotinnerVariables usedUnbind from lexicalenvironment (similarly, spidermonkey is not unbound and only assignedundefined), And the remaining variables are retained.

evalImpact code
function outer() {    var largeObject = LargeObject.fromSize('100MB');    return function() {        eval('');        debugger;    };}var inner = outer();inner();
Test Results
  • Jscript. dll-no recovery, no reduction in memory.
  • Chakra-no recovery, no reduction in memory.
  • Carakan-No recycle, no drop in memory.
  • V8-do not recycle, accesslargeObjectYou can get the correct value.
  • SpiderMonkey-do not recycle, accesslargeObjectYou can get the correct value.
Conclusion

IfinnerUsed in FunctionsevalTo avoid unexpected results.

Indirect call evalUse Code
function outer() {    var largeObject = LargeObject.fromSize('100MB');    return function() {        window.eval('');        debugger;    };}var inner = outer();inner();
Test Results
  • JScript. dll-no recovery, no reduction in memory.
  • Chakra-reclaim, the memory will be restoredouterThe status before the function is executed.
  • Carakan-No recycle, no drop in memory.
  • V8-recycle, accesslargeObjectThrow a ReferenceError.
  • SpiderMonkey-recycle, accesslargeObjectGetundefined.
Conclusion

Because ecmascript requires indirect callsevalThe code will beGLOBAL SCOPECannot accesslargeObjectVariable. Therefore, for indirect callsevalIn this case, each JavaScript Engine will handle it in a standard way, regardless of the indirect call.eval.

Similarly,new Function('return largeObject;')In this case, due to the standardnew FunctionThe[[Scope]]Is a global lexicalenvironment, and thus cannot be accessedlargeObject, All engines are called indirectlyevalMethod, select ignoreFunctionThe call of the constructor.

Use code for multiple nested Functions
function outer() {    var largeObject = LargeObject.fromSize('100MB');    function help() {        largeObject;        // eval('');    }    return function() {        debugger;    };}var inner = outer();inner();
Test Results
  • JScript. dll-no recovery, no reduction in memory.
  • Chakra-no recovery, no reduction in memory.
  • Carakan-No recycle, no drop in memory.
  • V8-do not recycle, accesslargeObjectYou can get the correct value.
  • SpiderMonkey-do not recycle, accesslargeObjectYou can get the correct value.
Conclusion

Not only is it returnedinnerFunction, ifouterNestedhelpUsed in the functionlargeObjectVariable (or directly calleval).largeObjectVariable cannot be recycled. Therefore, javascript Engine scans more than justinnerThe function source code also scans the source code of all other nested functions to determine whether a specific variable can be unbound.

Use withExpression Code
function outer() {    var largeObject = LargeObject.fromSize('100MB');    var scope = { o: LargeObject.fromSize('100MB') };    with (scope) {        return function() {            debugger;        };    }}var inner = outer();inner();
Test Results
  • JScript. dll-no recovery, no reduction in memory.
  • Chakra-RecyclelargeObject, But do not recyclescope.o, Memory recoveryouterThe function is increased by about MB before it is called (unknown ).scopeWhether it is recycled ).
  • Carakan-No recycle, no drop in memory.
  • V8-do not recycle, accesslargeObjectAndscopeAndoYou can get the correct value.
  • SpiderMonkey-RecyclelargeObjectAndscopeTo access the two variables.undefined, Do not recycleoTo obtain the correct value.
Conclusion

WhenwithWhen the expression is used, V8 will discard the collection of all variables and retain the binding of all variables in LexicalEnvironment. While SpiderMonkey will retainwithAll variables in the new LexicalEnvironment generated by the expression are bound.outerThe LexicalEnvironment generated by the function is processed in a standard manner, and the variable binding is removed as much as possible.

Use catchExpression Code
function outer() {    var largeObject = LargeObject.fromSize('100MB');    try {        throw { o: LargeObject.fromSize('100MB'); }    }    catch (ex) {        return function() {            debugger;        };    }}var inner = outer();inner();
Test Results
  • Jscript. dll-no recovery, no reduction in memory.
  • Chakra-Reclaim largeobject and ex, and the memory will be restored to the status before the outer function is called.
  • Carakan-No recycle, no drop in memory.
  • V8-only reclaim largeobject, access largeobject and throw a referenceerror, but still can access ex.
  • Spidermonkey-only recycles largeobject. undefined is obtained when accessing largeobject, but ex is still accessible.
Conclusion

catchAlthough the expression will add a LexicalEnvironment, it has almost no effect on the algorithm for unbinding variables in the closure. This is becausecatchThe generated LexicalEnvironment only appends a bound catch Error object, which is controllable (relativewithTherefore, the impact on variable recovery can also be controlled and optimized. However, for LexicalEnvironment with newly generated and added Error objects, V8 and SpiderMonkey will not be further optimized and recycled, while Chakra will process the LexicalEnvironment. If the Error object can be recycled, then it is unbound.

Code used for variable with the same name declared in nested Functions
Function outer () {var largeobject = largeobject. fromsize ('100mb'); return function (largeobject/* or declare */In the function body) {// var largeobject ;};} var inner = outer (); inner ();
Test Results
  • Jscript. dll-no recovery, no reduction in memory.
  • Chakra-recycle, the memory will be restored to the status before the outer function is called.
  • Carakan-No recycle, no drop in memory.
  • V8-recycle, the memory will be restored to the status before the outer function is called.
  • Spidermonkey-recycle, the memory will be restored to the status before the outer function is called.
Conclusion

When a nested function has a variable or parameter with the same name as an outer function, the optimization of variable recycling in the outer function is not affected. That is, the javascript engine will excludeFormalparameterlistAnd allVariabledeclarationIn the expressionIdentifierAnd then scan allIdentifierTo analyze the recyclability of variables.

Overall conclusion

The first clear conclusion is that the following content will affect the collection of variables in the closure:

  • Whether this variable is used in nested functions.
  • Nested functions?Direct calleval.
  • Used?withExpression.

Chakra, V8, and spidermonkey will be affected by the above factors and show different and similar recycling policies, while JScript. DLL and carakan are not optimized in this respect at all, and all variables in the entire lexicalenvironment will be fully retained, resulting in a certain amount of memory consumption.

Because chakra, V8, and spidermonkey engines with optimization policies for the collection of variables in the closure are similar in behavior, we can summarize the following: when a function is returnedfnHour:

  1. IffnOf[[Scope]]Is ObjectEnvironment (withExpressions generate ObjectEnvironment, functions, andcatchExpression to generate DeclarativeEnvironment), then:

    1. If the engine is V8, the entire process is exited.
    2. For SpiderMonkey, the outer LexicalEnvironment of the ObjectEnvironment is processed.
  2. Obtain all Function objects under the current LexicalEnvironment. For each Function object, analyze its FunctionBody:

    1. If FunctionBody containsDirectly call evalTo exit the entire process.
    2. Otherwise, all identifiers are obtained.
    3. For each Identifier, set itnameFind the rule namednameBindingbinding.
    4. PairbindingAddnotSwapAttribute, whose value istrue.
  3. Check the binding of each variable in the current LexicalEnvironment. If the binding hasnotSwapProperty and the value istrue, Then:

    1. If it is a V8 engine, delete the binding.
    2. For SpiderMonkey, set the bound valueundefined, Will be deletednotSwapAttribute.

    For the Chakra engine, it is unknown whether it is in V8 or SpiderMonkey mode.

From the above tests and conclusions, V8 is indeed an excellent JavaScript engine, and the optimization in this aspect is quite in place. The spidermonkey adopts a more friendly method. Instead of directly deleting the variable binding, it assigns the valueundefinedThe spidermonkey team may consider someExtremely SpecialThis variable may still be used, so at least a referenceerror will not be thrown to interrupt code execution. Compared with the jscript. dll of IE8, the chakra of ie9 has made great progress, and the detailed processing is also excellent. Opera's carakan is relatively backward in this aspect and has not optimized the variable recycling in the closure at all. It chose the safest but slightly wasteful method.

In addition, all browsers with optimization policies have chosen a balance between overhead and speed, which is exactly why the "multiple nested functions" test case, althoughinnerUnusedlargeObjectObject, even ininnerInhelpThe function object has been unbound but not unbound.largeObject. Based on this phenomenon, it can be inferred that each engine only checks the relevance of a layer, that is, it does not processinner -> help -> largeObjectIn this way, you can only find the reference relationship in depth.inner -> largeObjectAndhelp -> largeObjectAnd make a collection to improve efficiency. Maybe there is still a waste of memory overhead in this way, but the CPU resources are also very valuable. How to grasp the balance between them is the choice of the javascript engine.

In addition, based on tests by some developers, Chakra is even eligible to be called an existingThe fastest javascript EngineMicrosoft has been working hard, and developers should not blindly abuse and laugh at IE. We can laugh at IE6's backwardness, and we can't see the contributions that earlier versions of Internet Explorer have made to the development of the Internet. We can relentlessly crack down on these historical products today, however, we should not treat the entire IE series equally and stick the "junk" name. An objective view and evaluation are the most basic principles and qualities that a technician should possess.

----------------------------------------- Original address -----------------------------------------------

Closure and variable recycling

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.