How to troubleshoot JS Memory leakage (Chrome Profiles)

Source: Internet
Author: User

Google Chrome provides powerful JS debugging tools. Heap Profiling is one of them. Heap Profiling can record the current heap memory (Heap) snapshot and generate the object description file, which provides all the objects used for JS running at that time, and these pairs...

I. Overview
Google Chrome provides powerful JS debugging tools. Heap Profiling is one of them. Heap Profiling can record the current heap memory (Heap) snapshot and generate the object description file, which provides all the objects used for JS running at that time, and the memory size and reference hierarchical relationships of these objects. These description files provide useful information for Memory leakage troubleshooting.
Note: All examples in this article are based on Google Chrome.
What is heap?
When JS is running, there will be stack memory and heap memory. When we instantiate a class with new, the new object will be saved in heap, the reference of this object is stored in the stack. The program finds this object through the reference in the stack. For example, var a = [, 3];, a is a reference stored in the stack, and heap stores an Array object with content of [, 3.
Ii. Heap Profiling
Open tools
Open Chrome (version 25.0.1364.152 m), open the website to be monitored (this example uses the game hall), press F12 to bring up the debugging tool, and click the "Profiles" tab. You can see:

You can see that this panel can monitor CPU, CSS, and memory, select "Take Heap Snapshot", and click "Start" to Take the heap Snapshot of the current JS, as shown in:

The right view lists the objects in heap. Since the game hall uses the Quark game library, we can clearly see the class names such as Quark. XXX (that is, the reference name of the Function object ).
Note: The GC is automatically executed before each snapshot is taken, so the objects in the view are reachable.
View explanation
Column field explanation:
Constructor -- class name Distance -- estimated the reference level Distance from the object to the root
Objects Count -- shows the number of Objects in this class.
Shallow Size -- Memory occupied by objects (excluding memory occupied by other objects referenced internally) (unit: bytes)
Retained Size -- total memory occupied by objects (including memory occupied by other objects referenced internally) (unit: bytes)

The following explains the meaning of the category name:
(Compiled code) -- unknown, estimated to be the code area
(Closure) -- closure (array) -- unknown
Object -- JS Object type (system) -- unknown
(String) -- string type. Sometimes a new attribute is added to an object, and the attribute name appears here.
Array -- JS Array type cls -- Inheritance class exclusive to the game hall
Window -- JS window object
Quark. DisplayObjectContainer -- display container class of the Quark Engine
Quark. ImageContainer -- image class of the Quark Engine
Quark. Text -- the Text class of the Quark Engine
Quark. ToggleButton -- switch button class of the Quark Engine

For the cls class name, the reference name "cls" is used in the Inheritance Mechanism of the game hall, pointing to the newly created inheritance class, therefore, all objects instantiated by classes using this inheritance mechanism are put here. For example, if the program contains a class ClassA that inherits Quark. Text, the new object is stored in cls, not Quark. Text.

View object content
Click the triangle on the left of the class name to view all the objects of the class. The "@ 70035" after the object indicates the ID of the object (some may mistakenly think it is the memory address. After GC is executed, the memory address will change, but the Object ID will not ). When you move the cursor over an object, the internal attributes and current values of the object are displayed.

This view helps us identify the object. However, this view cannot be referenced by anyone.

View object reference relationships
Click an object to view the reference hierarchical relationship of the object, for example:

The Object's retaining tree view displays the objects referenced by the Object and the referenced name. The object in the figure is referenced by five objects:
1. The _ txtContent variable of a cls object;
2. context variable of a closure function;
3. self variable of the same closure function;
4. Zero Position of an array object;
5. target variable of a Quark. Tween object.

You can see the two references of context and self to know the Quark. the Text object uses the context binding mechanism commonly used by JS and is referenced by variables in a closure, which is equivalent to the Quark. two references are added to the Text object, which is prone to memory leakage. If the closure function is not released, this Quark. text objects cannot be released.
Expand _ textContent to see references at the next level:

Looking at this tree chart in turn, we can see that one of the reference chains of this object (ID @ 70035) is like this:
GameListV _ curV _ gameListV omitted... \ |/_ noticeWidget | _ noticeC | _ noticeV | _ txtContent | Quark. Text @ 70035
Through the snapshot comparison function, you can know which objects have changed during the running of the program.
I have taken a snapshot just now. I will take another snapshot next time, for example:

Click the black solid circle button in the figure to get the second memory snapshot:

Click "Snapshot 2" in the figure to switch the view to the second Snapshot.

Click "Summary" in the figure to bring up a list and select the "Comparison" option. The result is as follows:

This view lists the differences between the current view and the objects in the previous view. Column Name field explanation: # New -- how many New objects are created # Deleted -- how many objects are recycled # Delta -- object change value, that is, the number of newly created objects minus the number of recycled objects. Size Delta -- the memory Size (in bytes) changes. Pay attention to the Delta field, especially for objects with a value greater than 0. The following uses Quark. Tween as an example to expand the object, as shown in:

In the "# New" column, if "." exists, it indicates a New object.
In the "# Deleted" column, if "." exists, the object is recycled.
When troubleshooting problems, you should take several more snapshots for comparison, which helps to find out the rules.

Iii. Memory leakage troubleshooting
When the memory of a JS program overflows, it will permanently invalidate a function (depending on which function is run by the JS Code at that time). Generally, the result is that the program suddenly gets stuck or an exception occurs.
In this case, we need to investigate the memory leakage of the JS program and find out which objects occupy the memory that has not been released. These objects are usually released by developers, but they are still referenced by a closure or put in an array.

Memory leakage caused by observer Mode
Sometimes we need to add the Observer mode (Observer) to the program to solve some modules. However, improper use may cause memory leakage.
To troubleshoot this type of Memory leakage, focus on the referenced object types: closure and Array objects.
The following uses the Texas hold'em game as an example:


The tester found that the Texas hold'em game had a memory overflow problem. Repeat the steps: Go to the game-exit to the partition-then enter the game-and then exit to the partition, the game gets stuck several times.
The Troubleshooting steps are as follows:
1. Open the game;
2. Enter the first partition (fast field 5/10 );
3. After Entering, take a memory snapshot;
4. Exit the partition interface;
5. Enter the same partition again;
6. Take a memory snapshot again;
7. Repeat steps 2 to 6 until five groups of memory snapshots are taken;
8. Convert the views in each group to the Comparison view;
9. compare and analyze the memory.
After the above steps, you can get the result:

Let's take a look at the last snapshot. We can see the closure (closure) + 1, which is the important part. (String), (system), and (compiled code) types can be ignored, because there is not much information provided.

Click the second to the last snapshot to see that the closure type is also + 1.

Next, let's look at the last snapshot. The closure is still + 1.
This indicates that this closure function will be created every time you enter the game and is not destroyed when you exit the partition.
Closure shows many function objects:

The number of new closures is 49, and the number of recycled closures is 48, that is, 48 closures are correctly released in this operation, and one of them is forgotten to be released. The IDS of each newly created and recycled function object are different. No associations are found and the closure function is not located.
Next, open the Object's retaining tree view and check whether there is an ever-increasing array in the reference.
For example, expand "Snapshot 5" to reference each function object:

The reference of a function object deleFunc is stored in an array. The subscript is 4, and the Object ID of the array is @ 45599.
Continue searching for the function object of "Snapshot 4:

It is found that the reference name of a function is also deleFunc, which is also stored in the array with ID @ 45599, And the subscript is 3. This object is probably a closure that has not been released.
Continue to view the function object in "Snapshot 3:

We can see that the subscript of the same function object is 2. Therefore, there must be a memory leakage problem. There is a reference name "login_success" under the array. Search for this keyword in the program and locate the problematic code. Because the "login_success" notification was registered when you entered the game: ob. addListener ("login_success", _ onLoginSuc); but this notification is not removed when you exit the partition. The next time you enter the game, you register the notification again, which leads to the increasing number of functions. This notification is removed when you exit the partition: ob. removeListener ("login_success", _ onLoginSuc); this will solve the memory leakage problem.
The Texas hold'em problem is most common in the observer design mode. A global array is used to store all registered notifications. If you forget to remove the notifications, the array will increase continuously, resulting in memory overflow.

Memory leakage caused by context binding
Most of the time, we will use the context binding function bind (or some people write it as delegate). Whether it is the self-implemented bind method or the JS native bind method, there will be a risk of Memory leakage.
The following is a simple example:
<Script type = "text/javascript">
Var ClassA = function (name ){
This. name = name;
This. func = null;
};

Var a = new ClassA ("");
Var B = new ClassA ("B ");

B. func = bind (function (){
Console. log ("I am" + this. name );
}, );

B. func (); // output I am

A = null; // release
// B = null; // Release B

// Simulate context binding
Function bind (func, self ){
Return function (){
Return func. apply (self );
};
};
</Script>
In the above Code, bind saves context self through the closure, so that this in Event B. func points to a rather than B.
First, comment out B = null, and only release. Take a look at the memory snapshot:


We can see that there are two ClassA objects, which are inconsistent with our intention. We have released a, and only one ClassA object B should be available.

From the above two figures, we can see that one of the two objects is B, and the other is not a, because the reference of a has been left empty. The second ClassA object is the context self of the closure in bind. self and a reference the same object. Although a is released, because B is not released, or B. func is not released, the self in the closure still exists. To release self, run B = null or B. func = null.
Change the code:
<Script type = "text/javascript"> var ClassA = function (name) {this. name = name; this. func = null ;};
Var a = new ClassA ("a"); var B = new ClassA ("B ");
B. func = bind (function () {console. log ("I am" + this. name) ;}, );
B. func (); // output I am a = null; // release
B. func = null; // release self
// Simulate context binding function bind (func, self) {return function () {return func. apply (self) ;};</script>
Let's look at the memory:

We can see that there is only one ClassA object B, and a has been released.

Iv. Conclusion
The flexibility of JS is both an advantage and a disadvantage. Pay attention to the memory leakage when writing code. When the amount of code is very large, you cannot simply review the code to troubleshoot the problem. You must have some monitoring and comparison tools to help troubleshoot the problem.
The following common situations are summarized during memory leakage troubleshooting:
1. The closure context is not released after binding;
2. After the notification is added, the observer mode is not cleared in time;
3. The timer processing function was not released in time and the clearInterval method was not called;
4. Some controls on the view layer are added and not removed.

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.