Class 4 JavaScript memory leaks and how to avoid

Source: Internet
Author: User
Tags setinterval microsoft edge

Original: 4 Types of Memory Leaks in JavaScript and what to Get Rid of them
NOTES: Doodle Code Dragon

Translator Note: This article does not translate word for word, but the information I think important is translated. If you are proficient in English, you can read the original text directly.

This article explores common client-side JavaScript memory leaks and how to use Chrome development tools to discover problems.

Brief introduction

Memory leaks are the ultimate problem for every developer, and it is the root of many problems: slow response, crashes, high latency, and other application issues.

What is a memory leak?

In essence, memory leaks can be defined as: When an application no longer needs to consume memory, for some reason, memory is not reclaimed by the operating system or the pool of available memory. Programming languages manage memory in different ways. Only the developer knows which memory is not needed and the operating system can recycle it. Some programming languages provide language features that can help developers do such things. Others hope to see if the developer needs to be clear about the memory.

JavaScript Memory Management

JavaScript is a garbage-collected language. The garbage collection language helps developers manage memory by periodically checking to see if previously allocated memory is up. In other words, the garbage collection language mitigates the issue of "memory is still available" and "Memory is still available". The difference between the two is subtle and important: Only the developer knows which memory will still be used in the future, and the memory cannot be determined and tagged by the algorithm, and is recycled by the operating system at the appropriate time.

JavaScript memory leaks

A memory leak in a garbage-collected language is a reference that is not required. Before you understand it, you need to know how the garbage collection language can identify the unreachable and unreachable memory.

Mark-and-sweep

Most garbage-collected languages use algorithms called Mark-and-sweep. The algorithm consists of the following steps:

    1. The garbage collector creates a "roots" list. Roots is typically a reference to a global variable in your code. In JavaScript, the "window" object is a global variable that is treated as root. The Window object always exists, so the garbage collector can check that it and all its child objects exist (that is, not garbage);
    2. All roots are checked and marked as active (i.e. not garbage). All sub-objects are also checked recursively. All objects that start with root are not considered garbage if they are accessible.
    3. All unmarked memory is treated as garbage, and the collector can now free up memory and return it to the operating system.

The modern garbage collector has improved the algorithm, but the essence is the same: the memory is marked and the rest is garbage collected.

An unwanted reference is that the developer knows that the memory reference is no longer needed, but for some reason it is still left in the active root tree. In JavaScript, an unwanted reference is a variable that is kept in the code, it is no longer needed, but it points to a piece of memory that should have been freed. Some people think this is a developer's mistake.

In order to understand the most common memory leaks in JavaScript, we need to know which way references are easy to forget.

Three types of common JavaScript memory leaks 1: unexpected global variables

JavaScript handles undefined variables in a looser way: Undefined variables create a new variable in the global object. In the browser, the global object is window .

1
2
3
Foo (Arg) {
"This is a hidden global variable";
}

The truth is:

1
2
3
Foo (Arg) {
"This was an explicit global variable";
}

The function foo forgets to use it var , creating a global variable unexpectedly. This example leaks a simple string, which is harmless, but has a worse situation.

Another unexpected global variable may be created by this :

 1 
2
3
4
5
6
7
  function foo (  this.variable =  "potential accidental global"; 

//Foo calls itself, this Points to the Global Object (window)
//instead of undefined
foo ();

Adding the header to the JavaScript file ‘use strict‘ prevents such errors from occurring. Enable strict mode parsing JavaScript to avoid accidental global variables.

Global Variables Considerations

Although we discussed some of the unexpected global variables, there are still some explicit global variables that generate garbage. They are defined as not recyclable (unless defined as empty or redistributed). Especially when global variables are used to store and process large amounts of information temporarily, you need to be careful. If you must use global variables to store large amounts of data, be sure to set it to null or redefine later. One of the main reasons for increasing memory consumption associated with global variables is the cache. The cache data is for reuse, and the cache must have a maximum size to use. High memory consumption causes the cache to break above the limit because the cached content cannot be reclaimed.

2: Forgotten timer or callback function

It is very common to use in JavaScript setInterval . A common code:

1
2
3
4
5
6
7
8
var someresource = GetData ();
SetInterval (function () {
document.getElementById (' Node ');
if (node) {
Working with node and Someresource
Json.stringify (Someresource));
}
1000);

This example shows what: a timer associated with a node or data is no longer needed, the node object can be deleted, and the entire callback function is not required. However, the timer callback function is still not recycled (the timer stops before it is recycled). At the same time, someResource if a large amount of data is stored, it cannot be recycled.

For the Observer's example, it is important to explicitly remove them once they are no longer needed (or the associated objects become unreachable). The old IE 6 is not able to handle circular references. Now, even if they are not explicitly removed, most browsers can reclaim the observer handler function once the Observer object becomes unreachable.

Observer code Example:

1
2
3
4
5
6
document.getElementById (' button ');
OnClick (event) {
' Text ';
}

Element.addeventlistener (' click ', OnClick);

Object Observer and Circular reference considerations

Older versions of IE are unable to detect circular references between DOM nodes and JavaScript code, which can lead to memory leaks. Today, modern browsers, including IE and Microsoft Edge, use more advanced garbage collection algorithms that can detect and process circular references correctly. In other words, when you reclaim node memory, you do not have to call removeEventListener it.

3: Out of the DOM reference

Sometimes it is useful to save the DOM node internal data structure. If you want to update a few lines of the table quickly, it is meaningful to save each row of the DOM as a dictionary (a JSON key-value pair) or an array. At this point, the same DOM element has two references: one in the DOM tree and the other in the dictionary. In the future when you decide to delete these rows, you need to clear all two references.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var elements = {
buttondocument.getElementById (' button '),
Imagedocument.getElementById (' Image '),
Textdocument.getElementById (' Text ')
};

functionDostuff () {
IMAGE.SRC = ' http://some.url/image ';
Button.Click ();
console.log (text.innerhtml);
//more logic
}

function removebutton ( //button is the descendant element of body
Document.body.removeChild (document.getelementbyid (
//at this point, there is still a global reference to the #button
//elements dictionary. The button element is still in memory and cannot be reclaimed by GC.
}
/span>

Also consider reference issues within the DOM tree or child nodes. If your JavaScript code holds a <td> reference to one of the tables. When you decide to delete the entire table in the future, it's intuitive to assume that the GC reclaims <td> other nodes than the saved. This is not the case: this <td> is a child node of the table, and the child element is a reference relationship with the parent element. Because the code retains <td> the reference, the entire table remains in memory. Be cautious when saving DOM element references.

4: Closures

Closures are a key aspect of JavaScript development: Anonymous functions can access variables of the parent scope.

code example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var thething =Null
var replacething =function () {
var originalthing = thething;
var unused =function ( if ( originalthing)
console.log ( };

thething = {
longstr: array (1000000). Join ( SomeMethod: function ( console.log (somemessage);
};
};
setinterval (replacething, 1000);

The code snippet does one thing: Each call replaceThing ,theThing gets a new object that contains a large array and a new closure ( someMethod ). At the same time, the variable unused is a reference originalThing closure (formerly replaceThing called Again theThing ). Is your mind in disarray? The most important thing is that once the scope of the closure is created, they have the same parent scope, and the scope is shared. someMethodIt can be theThing used, someMethod with unused a shared closure scope, although unused never used, which it references to originalThing force it to remain in memory (to prevent it from being recycled). When this code runs repeatedly, you will see a rising memory footprint, and the garbage collector (GC) does not reduce memory consumption. Essentially, a linked list of closures has been created, and each closure scope carries an indirect reference to a large array, causing a serious memory leak.

Meteor's blog post explains how to fix this problem. At replaceThing the end of the add originalThing = null .

Chrome Memory Profiling Tools Overview

Chrome offers a great set of tools for detecting the memory footprint of JavaScript. Two important tools related to memory: timeline and profiles .

Timeline

Timeline can detect memory that is not needed in the code. In this case, we can see that the potential for the leakage of the object of steady growth, the data acquisition at the end of the memory consumption is significantly higher than the initial acquisition, node (nodes) The total is also very high. There are various indications that the DOM node leaks in the code.

Profiles

Profiles is a tool that you can spend a lot of time focusing on, which can save snapshots, compare different snapshots used by JavaScript code memory, or record time allocations. Each result contains a list of different types, a list of summary (summary) and a list of comparison (controls) related to memory leaks.

The summary (Summary) list shows the allocation and total size of different types of objects: Shallow size (the total size of all objects of a particular type), retained size (shallow size plus other object sizes associated with this). It also provides a concept of the distance between an object and the associated GC root.

A memory leak can be found by comparing the comparison list of different snapshots.

Example: Using Chrome to discover memory leaks

There are essentially two types of leaks: a leak caused by periodic memory growth, and an even-present memory leak. It is obvious that periodic memory leaks are easy to spot, and that occasional leaks are tricky, generally easy to overlook, and occasionally one that may be considered an optimization problem, and periodic occurrences are considered a must-have.

Take the code in the Chrome document as an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21st
var x = [];

functionCreatesomenodes () {
var Div,
i =100,
Frag =Document.createdocumentfragment ();

for (; i >0; i--) {
div =Document.createelement ("Div");
Div.appendchild (document.createTextNode (i + "-" + new date (). totimestring ());
Frag.appendchild (div);

document.getelementbyid ( "nodes"). AppendChild (Frag);
functi On grow ( X.push (new array (1000000). Join ( Createsomenodes ();
SetTimeout (Grow,1000);

/span>

When grow executed, the DIV node is created and inserted into the DOM, and a large array is assigned to the global variable. The above mentioned tools can detect a steady rise in memory.

Find out the periodic growth of memory

Timeline tags are good at doing this. Open the example in Chrome, open Dev Tools, switch to timeline, tick memory and click the Record button, then click on the button on the page The Button . Stop recording after a while to see the result:

There are two indications of a memory leak, the Nodes (Green Line) and the JS heap (blue line) in the diagram. The steady growth of Nodes has not declined, which is a significant signal.

The memory footprint of the JS heap is also growing steadily. Due to the impact of the garbage collector, it is not so easy to find. The figure shows that memory consumption is up and down, in fact, after each fall, the JS heap size is larger than the original. In other words, although the garbage collector constantly collects memory, the memory is periodically leaked.

After determining that there is a memory leak, we look for the root cause.

Save two snapshots

Switch to the Profiles tab of Chrome Dev Tools, refresh the page, and when the page refreshes, click Take Heap Snapshot to save the snapshot as a baseline. Then click The Button the button again and wait a few seconds to save the second snapshot.

Filter menu Select Summary, right select Objects allocated between Snapshot 1 and Snapshot 2, or Filter menu select Comparison, then you can see a comparison list.

This example is easy to find a memory leak, looking at (string) the Size Delta constructor,8mb,58 of a new object. The new object is assigned, but not released, taking up 8MB.

If you expand (string) Constructor, you will see a number of separate memory allocations. Select a separate assignment and the following retainers will draw our attention.

The assignment we have selected is part of the array, and the array is associated to window the object's x variable. This shows the full path from the huge object to the root () that cannot be reclaimed window . We have found a potential leak and its provenance.

Our example is simple, revealing only a small number of DOM nodes, and using the snapshots mentioned above is easy to find. For larger sites, Chrome also provides the Record Heap allocations feature.

Record heap allocations for memory leaks

Go back to the Profiles tab of Chrome Dev Tools and click on the Record Heap allocations. When the tool is running, note the blue bar at the top, which represents the memory allocation, with a large amount of memory allocated per second. Run a few seconds later to stop.

You can see the tool's killer: Select a timeline to see the memory allocations for this time period. When possible, select a timeline that is close to the peak, and the following list shows only three constructor: one is the most compromised (string) , the next is the associated DOM assignment, and the last is Text the constructor (the text that the DOM leaf node contains).

Select a constructor from the list HTMLDivElement and select Allocation stack .

Now that you know where the element is assigned to (- grow createSomeNodes ), look closely at the timeline in one, and discover that constructor has been HTMLDivElement called many times, meaning that the memory is always occupied and cannot be recycled by GC, we know exactly where these objects are assigned ( createSomeNodes )。 Go back to the code itself and explore how to fix the memory leaks.

Another useful feature

In the result area of the heap allocations, select Allocation.

This view presents a list of functions related to memory allocation, and we immediately see the grow and createSomeNodes . When choosing grow , look at the relevant object constructor, clearly seen (string) , HTMLDivElement and Text leaked out.

With the tools mentioned above, memory leaks can be easily found.

Attachment jpg change rar

Class 4 JavaScript memory leaks and how to avoid

Related Article

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.