JavaScript execution context and execution stack
The type of execution context
There are three types of execution contexts:
-
Global execution context: there is only one. The global object in the browser is the Window object. This points to this global object.
-
Function execution contexts: There are an infinite number of them, which are created only when a function is called, and a new execution context is created each time a function is called.
-
Eval function execution context: Refers to code that runs in the Eval function, rarely used and not recommended.
Execution stack
The execution stack, also known as the call stack, has a LIFO (Last in, first out) structure for storing all execution contexts created during code execution. When JS code is first run, a global execution context is created and pushed into the current execution stack. Each time a function call occurs, the engine creates a new function execution context for that function and pushes it to the top of the current execution stack. According to the execution stack LIFO rule, when the top function is completed, its corresponding function execution context will Pop out of the execution stack, and the control of the context will be moved to the next execution context of the current execution stack.
Create the execution context
The execution context is created in two stages: 1) the create stage; 2) Execution phase
Create a stage
1. Determine the value of this, also known as this Binding.
2. The LexicalEnvironment component is created.
3. The VariableEnvironment component is created.
This Binding
In the global execution context, the value of this points to the global object, in the browser to the window object, and in nodeJS to the module object of the file.
In the context of function execution, the value of this depends on how the function is called. There are: default binding, implicit binding, explicit binding (hard binding), new binding, arrow function.
Lexical Environment
- The lexical environment has two components
1. Environment record: Store the actual location of variable and function declarations
2, reference to the external environment: you can access its external lexical environment
- There are two types of lexical environments
Global environment: is a lexical environment with no external environment, whose external environment reference is NULL. The value of this refers to a global object (window object) with its associated methods and properties (such as array methods) and any user-defined global variables.
Function environment: Variables defined by the user in a function are stored in the environment record, which contains arguments objects. A reference to an external environment can be a global environment or an external function environment that contains internal functions.
The variable environment
The variable environment is also a lexical environment, so it has all the attributes of the lexical environment defined above.
In ES6, the difference between lexical and variable environments is that the former is used to store function declarations and variable (let and const) bindings, while the latter is only used to store variable (var) bindings.
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20.30);
Copy the code
Execution context:
GlobalExectionContext = {
ThisBinding: <Global Object>, LexicalEnvironment: {EnvironmentRecord: {Type: "Object", // the identifier is binding here < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: C: undefined,} outer: <null>}} FunctionExectionContext = {ThisBinding: <Global Object>, LexicalEnvironment: {EnvironmentRecord: {Type: "Declarative", // 30, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: G: undefined, outer: <GlobalLexicalEnvironment>}}Copy the code
Why variables are promoted: During the creation phase, function declarations are stored in the environment and variables are set to undefined (in the case of var) or remain uninitialized (in the case of let and const). So this is why it is possible to access variables defined by var (albeit undefined) before the declaration, but if you access variables defined by let and const before the declaration, you will be prompted with reference errors. This is called variable lifting.
Execution phase This phase completes the allocation of all variables and finally executes the code. If the Javascript engine cannot find the value of the let variable at the actual location declared in the source code, it will be assigned a undefined value.
JavaScript memory space
JavaScript memory space includes stack, heap, and pool. The stack holds variables, heap holds complex objects, and pool holds constants, so it is also called constant pool.
Variables in closures are not stored in mid-stack memory, but in heap memory.
Storage of variables
- Basic types –> are stored in stack memory, since these types occupy a fixed amount of memory and are accessed by value. There are six basic types: Undefined, Null, Boolean, Number, String, and Symbol.
- 2, the reference type –> is stored in the heap memory, because the size of such values is not fixed, so they cannot be stored in the stack memory, but the size of the memory address is fixed, so it is stored in the heap memory, in the stack memory is only the access address of the object. When querying a variable of reference type, the memory address is read from the stack and then the value in the heap is found by the address. For this, we call it access by reference.
In the data structure of computer, stack is faster than heap. Object is a complex structure and can be expanded: array can be expanded, Object can add attributes, and can be added, deleted, changed, and searched. They are placed in the heap so as not to affect stack efficiency. Instead, it references the actual object in the heap and operates on it. So when looking for a reference type value, look on the stack first and then look on the heap. A few questions
Question 1:
var a = 20;
var b = a;
b = 30;
// What is the value of a?
Copy the code
Question 2:
var a = { name: 'Front-end development' }
var b = a;
b.name = 'advanced';
// what is the value of a.name
Copy the code
Question 3:
var a = { name: 'Front-end development' }
var b = a;
a = null;
// What is the value of b
Copy the code
- For problem 1, a and B are both basic types, and their values are stored on the stack. A and B have their own independent stack space, so the value of A does not change after the value of B is changed.
- For problem 2, a and b are reference types, stack memory storage address pointing to the object in the heap memory, copy of a reference type for new variables automatically assigns a new value stored in the object, but is only the address of a pointer reference types, the actual point to the same object, so modify b.n ame values, The corresponding A.name changes.
- A = null; b = null; b = null; b = null;
The memory lifecycle of JavaScript
1, allocate the memory that you need 2, using the allocated memory (read, write) 3, when you don’t need to return its release, JavaScript has automatic garbage collection mechanism, the most commonly used are removed by marking algorithm to find which object is no longer continue to use, use a = null only actually made a release reference, The value of a is dereferenced and removed from the execution environment. This value will be found and freed the next time the garbage collector executes an operation. In a local scope, when the function is finished executing, there is no need for local variables to exist, so it is easy for the garbage collector to make judgments and reclaim. However, it is difficult to determine when global variables need to automatically free memory space, so avoid using global variables as much as possible during development.
JavaScript memory reclamation
JavaScript has an automatic garbage collection mechanism, where the garbage collector performs a free operation every once in a while to find values that are no longer in use, and then frees their memory.
- Destruction of local and global variables
Local variables: In a local scope, local variables are no longer necessary when the function is finished executing, so it is easy for the garbage collector to make judgments and reclaim them. Global variables: It can be difficult to determine when global variables need to automatically free memory, so avoid using them in your development.
- Take Google’s V8 engine, for example, where all JS objects are allocated via the heap.
Initial allocation: When a variable is declared and assigned a value, the V8 engine allocates it in heap memory. Continue requisition: When the allocated memory is insufficient to store the variable, the V8 engine continues to requisition memory until the heap size reaches the V8 engine’s memory limit.
- The V8 engine manages JS objects in the heap memory by generation
New generation: JS objects with short lifetime, such as temporary variables and strings. Old generation: objects that survive multiple garbage collections and have a long life cycle, such as master controller, server objects, etc.
Garbage collection algorithm
Garbage collection algorithm For garbage collection algorithms, the core idea is how to determine memory is no longer used, commonly used garbage collection algorithms have the following two.
Reference counting (no longer used in modern browsers) Tag clearing (common)
Reference counting
Reference counting algorithms define “memory out of use” simply by looking at whether an object has a reference to it. If no other object points to it, the object is no longer needed.
// Create an object person with two references to the attributes age and name
var person = {
age: 12.name: 'aaaa'
};
person.name = null; // Although name is set to null, name is not recycled because the Person object also has a reference to name
var p = person;
person = 1; // The original person object is assigned a value of 1, but since there is a new reference p to the original person object, it will not be reclaimed
p = null; // The original Person object is no longer referenced and will soon be reclaimed
Copy the code
One fatal problem with reference counting is circular references
If two objects refer to each other, even though they are no longer in use, the garbage collector does not collect them and may eventually result in a memory leak.
function cycle() {
var o1 = {};
var o2 = {};
o1.a = o2;
o2.a = o1;
return "cycle reference!"
}
cycle();
Copy the code
After the cycle function completes, objects o1 and O2 are actually no longer needed, but their references to each other still exist according to reference-counting rules, so this part of memory is not reclaimed. So modern browsers don’t use this algorithm anymore.
Mark clear
The most common method of garbage collection in JavaScript is mark-and-sweep. When a variable is entered into the environment (for example, declaring a variable in a function), the variable is marked as entered into the environment. Logically, you can never free up memory occupied by variables that enter the environment, because they may be used whenever the execution stream enters the corresponding environment. When a variable leaves the environment, it is marked as “out of the environment.” Variables can be marked in any way. For example, you can flip a particular bit to record when a variable entered the environment, or use a list of variables that entered the environment and a list of variables that left the environment to track which variable changed. At the end of the day, it doesn’t really matter how you mark variables, but what strategy you use. At run time, the garbage collector marks all variables stored in memory (of course, it can be marked in any way). It then unflags variables in the environment and those referenced by variables in the environment. Variables tagged after this point are considered to be ready for deletion because they are no longer accessible to variables in the environment. Finally, the garbage collector completes the memory cleanup, destroying the tagged values and reclaiming the memory they occupy. As of 2008, JavaScript implementations of Internet Explorer, Firefox, Opera, Chrome, and Safari all used a marked sweep garbage collection strategy (or similar) at different intervals.
The tag clearing algorithm defines “no longer used object” as “unreachable object”. That is, from the root (in JS is the global object) to periodically scan the object in memory, any object that can be reached from the root, reserved. Objects that are unreachable from the root are marked as unused and recycled later. An unreachable object includes the concept of an unreferenced object, but the reverse is not necessarily true. So the above example can be handled correctly by garbage collection. So now for major browsers, it’s just a matter of disconnecting the object that needs to be reclaimed from the root. The most common memory leaks are usually related to DOM element bindings:
email.message = documentThe createElement method (" div "); displayList.appendChild(email.message);// Remove the DOM element from the displayList later
displayList.removeAllChildren();
Copy the code
In the code above, the div element is cleared from the DOM tree, but it is also bound to the email object, so if the email object exists, the div element is kept in memory.
More on the specific token clearing algorithm used by the V8 engine.
JavaScript memory leak
For continuously running daemons, memory that is no longer needed must be released in a timely manner. Otherwise, the memory footprint increases, which can affect system performance at best or cause process crashes at worst. If memory is not released in time when it is no longer needed, it is called a memory leak.
1. Browser method
Open the developer tool, Select Memory and Select Timeline in the Select Profiling Type field on the right. Click the record button in the upper left corner. Various operations are carried out on the page to simulate the use of users. After a certain period of time, click the Stop button in the upper left corner, and the memory usage for that period will be displayed on the panel.
2. The command line method uses the Process. memoryUsage method provided by Node.
The best way to optimize memory footprint is to save only the necessary data for executing code. Once data is no longer useful, it’s best to release references to it by setting its value to null — a practice called dereferencing. This applies to most global variables and properties of global objects.
Four common TYPES of JS memory leaks
An undefined variable creates a new variable in the global object, as shown below.
function foo(arg) {
bar = "this is a hidden global variable";
}
Copy the code
Function foo forgets to use var internally. JS actually mounts bar onto the global object, accidentally creating a global variable.
function foo(arg) {
window.bar = "this is an explicit global variable";
}
Copy the code
Another unexpected global variable might be created by this.
function foo() {
this.variable = "potential accidental global";
}
// Foo calls itself, this refers to the global object (window)
// instead of undefined
foo();
Copy the code
Workaround: Add ‘use strict’ to the header of your JavaScript file and use strict mode to avoid unexpected global variables, in which case this points to undefined. If you must use a global variable to store a lot of data, be sure to set it to NULL or redefine it when you’re done using it.
2. Forgotten timer or callback function timer setInterval code is common
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// Process node and someResource
node.innerHTML = JSON.stringify(someResource)); }},1000);
Copy the code
The above example shows that the timer still points to the node or data when it is no longer needed. So even when the node is removed, the interval still lives and the garbage collector cannot collect it, and its dependencies cannot be collected unless the timer is terminated.
If you store the DOM as a dictionary (JSON key-value pairs) or an array, the same DOM element has two references: one in the DOM tree and one in the dictionary. Both references will need to be cleared in the future.
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text')};function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
// More logic
}
function removeButton() {
// The button is a descendant of the body
document.body.removeChild(document.getElementById('button'));
// At this point, there is still a global #button reference
// elements dictionary. The button element is still in memory and cannot be collected by GC.
}
Copy the code
If a reference to a table is saved in the code. When the decision is made to delete the entire table in the future, it is intuitive that the GC will reclaim any nodes other than those already saved. This is not the case: this is a child node of the table, and the child element is referenced to the parent element. The entire table is still in memory because of the reference that the code kept. So be careful when saving references to DOM elements.
Closures: Closures allow the execution environment to be preserved without being cleaned up by garbage collection.
Reference: pythontutor.com/visualize.h…