How JavaScript Works: Memory Management…
In this paper, a permanent link: https://github.com/AttemptWeb…
There are some deletions and modifications, but most of them are based on the original text. The main purpose of the translation is to clarify the garbage collection mechanism of JavaScript, and welcome to point out any problems.
Memory allocation in JavaScript
Now we’ll explain how the first step (allocating memory) works in JavaScript.
JavaScript relieves the developer of the burden of dealing with memory allocation – JavaScript performs memory allocation itself and declares values.
var n = 374; Var s = 'sessionStack '; Var o = {a: 1, b: null}; Function f(a) {return a + 3; } / / for the function allocates memory allocated memory someElement. / / function expression addEventListener (' click ', function () {someElement. Style. BackgroundColor = 'blue'; }, false);Copy the code
Use memory in JavaScript
Basically using allocated memory in JavaScript means reading and writing in it.
This can be done by reading or writing the value of a variable or object property, or even passing a variable to a function.
Garbage collection mechanism
Garbage collection is limited in implementing general problem solutions because it is virtually undecidable to discover whether some memory is “no longer needed.” The basic concepts of the main garbage collection algorithms and their limitations are explained below.
Memory references
An object is said to reference another object if it has access to another object (either implicitly or explicitly). For example, a JavaScript reference to its prototype (implicit reference) and its attribute value (explicit reference).
In this case, the concept of “object” extends to a much broader scope than normal JavaScript objects and includes function scope (or global lexical scope).
Lexical scope defines how variable names are resolved in nested functions: the inner function contains the scope of the parent function even after the parent function has returned.
Reference counting garbage collection
This is the simplest garbage collection algorithm. If there are zero references to it, the object is considered garbage collector. Take a look at the following code:
var o1 = { o2: { x: 1 } }; // Two objects are created. // The 'o1' object references the 'O2' object as its property. // Can not be garbage collected var o3 = o1; // 'o3' is the second variable that refers to the object pointed to by 'o1'. // Now, objects in 'o1' have only one reference, represented by the 'o3' variable var o4 = O3.o2; // This object now has two references: one for the property and one for the 'o4' variable O3 = '374'; // The object that was in "o1" is now zero and references to it can be garbage collected. // However, its' O2 'attribute exists and is referenced by the' o4 'variable, so it cannot be released. o4 = null; // The 'O2' attribute of the original object in 'o1' has zero reference to it. It can be garbage collected.Copy the code
Periodic generation problem
There is a limit to the cycle cycle. In the following example, two objects are created and referenced to each other, creating a loop. After a function call, they go out of bounds, so they are virtually useless and can be released. However, the reference-counting algorithm assumes that since each object is referenced at least once, neither object can be garbage collected.
function f() { var o1 = {}; var o2 = {}; o1.p = o2; // '02' o1 references o2 o2. P = o1; // 'o2' references' o2 '. A loop is created} f();Copy the code
Tagging and scanning algorithms
To determine whether an object is needed, the algorithm determines whether the object is accessible.
The tag and scan algorithm goes through these three steps:
The root node
In general, roots are global variables referenced in code. For example, in JavaScript, the global variable that can act as the root node is the “window” object. The global object in Node.js is called “global”. The complete list of root nodes is built by the garbage collector.- The algorithm then checks all root nodes and their children and marks them as active (meaning they are not garbage). Any variables that cannot be accessed by the root node are marked as garbage.
- In the end,
The garbage collector frees any chunks of memory that are not marked as active and returns them to the operating system
.
Visualization of tagging and scanning algorithm behavior. Mark and sweep
This algorithm is better than the previous one because “an object with zero references” makes the object unreachable. The exact opposite of what we see in cycles is not true. As of 2012, all modern browsers have tag-scanning garbage collectors built in. All of the improvements made last year in the JavaScript garbage collection (general/incremental/concurrent/parallel garbage collection) area were based on implementation improvements to this algorithm (tag and scan), but not improvements to the garbage collection algorithm itself, nor to the goal of determining whether an object is reachable.
Cycles are no longer an issue
In the above example, after the function call returns, the two objects are no longer referenced by variables in the global object. Therefore, the garbage collector considers them inaccessible.
Even if there are references between two objects, the root node does not access them.
Statistics garbage collector behavior
As convenient as garbage collectors are, they have their own set of policies. One is uncertainty. In other words, the GC (garbage collector) is unpredictable. You can’t be sure when a garbage collector will perform a collection. This means that in some cases, the program actually needs more memory. In other cases, in particularly sensitive applications, transience and stalling may be evident. Most GCS share a common pattern for garbage collection in allocation, although uncertainty means that it is impossible to determine when a garbage collector will perform a collection. If no allocation is performed, most GCS remain idle. Consider the following scenario:
A large number of assignments are performed.
Most (or all) of these elements are marked as inaccessible (assuming we revoke a reference to a cache we no longer need).
No deeper memory allocation is performed.
In this case, most GCS do not run any deeper collections. In other words, the collector does not collect these references even if there are variables available for collection. These are not strictly leaks, but can still result in higher than normal memory usage.
What is a memory leak?
A memory leak is a memory fragment that has been used by an application in the past but is no longer needed that has not yet been returned to the operating system or available memory pool. Because it is not released, it may cause the program to freeze and crash.
There are four common memory leaks in JavaScript
1: global variable
function foo(arg) {
bar = "some text";
// window.bar = "some text";
}
Copy the code
Assume that the purpose of bar is simply to refer to a variable in function foo. Declaring it without var, however, creates a redundant global variable.
You can do this by adding ‘use strict’ to the beginning of your JavaScript file; To avoid these consequences, this opens up a stricter JavaScript parsing mode that prevents accidental creation of global variables.
Unexpected global variables are certainly a problem, but more often than not, your code is affected by explicit global variables that cannot be collected by the garbage collector. Special attention needs to be paid to global variables used to temporarily store and process large amounts of information. If you must use global variables to store data, when you do, be sure to null or reassign them as soon as you are done using them.
2: forgotten timer or callback function
Take setInterval, which is often used in JavaScript.
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById('renderer'); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); }}, 5000); // Execute every 5 seconds.Copy the code
The code snippet above shows the consequences of using timers to reference nodes or useless data. It will neither be collected nor released. Unable to be collected by garbage collector, frequently called, occupying memory. The correct way to use them is to make sure that they are removed by explicit calls once the events that depend on them have been handled.
3: closures
Closures are a key aspect of JavaScript development: an internal function can access the variables of an external (enclosing) function.
var theThing = null; var replaceThing = function () { var originalThing = theThing; Var unused = function () {if (originalThing) // originalThing is used console.log("hi"); }; theThing = { longStr: new Array(1000000).join('*'), someMethod: function () { console.log("message"); }}; }; setInterval(replaceThing, 1000);Copy the code
Once the replaceThing function is called, theThing gets a new object that consists of a large array and a new closure (someMethod). OriginalThing, however, is referenced by a closure held by the unused variable (which is the Thing from the previous call to replaceThing). Remember that once a scope is created for a closure within the same parent scope, the scope is shared.
In this example, the scope created by someMethod is shared with unused. Unused contains a reference to originalThing. Even if unused is never referenced, someMethod can be used by theThing outside the scope of replaceThing (for example, somewhere globally). Since someMethod shares a closure range with unused, a unused reference to originalThing forces it to remain active (the entire shared range between two closures). This prevents their garbage collection.
In the example above, the scope created for the closure someMethod is shared with unused, which in turn references originalThing. SomeMethod can be referenced by theThing outside the scope of replaceThing, though unused is never referenced. In fact, a unused reference to originalThing requires it to remain active, since someMethod shares a closed range with unused.
All of this can lead to a lot of memory leaks. As the code snippet above runs over and over again, you can expect memory usage to rise. When the garbage collector is running, its size does not shrink. A chain of closures is created (in this case the root of which is the theThing variable), and each closure scope contains an indirect reference to the large array.
4: DOM overreference
In some cases, developers store DOM nodes in variables. Suppose you want to quickly update a few rows of a table. If you store a reference to each DOM row in an object, two references to the same DOM element are generated: one in the DOM tree and one in the object. If you decide to delete these lines, you need to remember to make both references inaccessible.
var elements = { button: document.getElementById('button'), image: document.getElementById('image') }; function doStuff() { elements.image.src = 'http://example.com/image_name.png'; } function removeImage() {// image is a direct child of body. document.body.removeChild(document.getElementById('image')); // We can still refer to button in the global element object. In other words, the button element is still in memory and cannot be collected by GC}Copy the code
There is an additional factor to consider when it comes to internal nodes or child nodes within a DOM tree. If you keep references to table table cells (TD tags) in your code and decide to remove the table from the DOM but keep references to td for that particular cell, you can expect a serious memory leak. You might think that the garbage collector would free everything except that cell TD. But that is not the case. Since cell TD is a child node of the table, and the child node keeps a reference to the parent node, this single reference to table td keeps the entire table in memory.
In SessionStack we try to follow these best practices and write code that handles memory allocation correctly for the following reasons:
Once you integrate SessionStack into your production environment’s Web application, it starts logging everything: all DOM changes, user interactions, JavaScript exceptions, stack traces, failed network requests, debug messages, etc.
Go through the SessionStack Web application for questions and view all user behavior. All of this must be done without a performance impact on your network application.
Since users can reload pages or navigate your application, all observers, interceptors, variable allocations, and so on must be handled correctly so that they do not cause any memory leaks or increase memory consumption for the Web application we are integrating.
Here’s a free plan so you can try it out.
Resources
How JavaScript works: memory management…
See more blog posts:
Personal gold digging more articles link jump;
GitHub blog project links to more articles jump;