I. Overview Google Chrome offers a very powerful JS debugging tool, and Heap profiling is one of them. The heap profiling can log the current heap memory (heap) snapshot and generate a description of the object, which gives all the objects that were used by the JS run at that time, the size of memory occupied by those objects, the hierarchical relationship of references, and so on. These profiles provide very useful information for troubleshooting memory leaks. Note: All the examples in this article are based on the Google Chrome browser. What is a heap JS Run, there will be stack memory (stack) and heap memory (heap), when we instantiate a class with new, the new object is stored in the heap, and the object's reference is stored in the stack. The program finds this object through a reference in the stack. For example, var a = [1,2,3];,a is a reference stored in a stack, and the heap stores an array object with the content as [+]. Second, Heap Profiling Open Tool Open the Chrome browser (version 25.0.1364.152 m), open the website you want to watch (take the game lobby as an example), press F12 to bring up the debugging tool, and click the "Profiles" tab. can see:
As you can see, this panel can monitor CPU, CSS and memory, select "Take Heap Snapshot", click "Start" button, you can capture the current JS Heap snapshot, as shown in:
The right-hand view lists the list of objects in the heap. Because the game Hall uses the Quark game library, it is clear to see the class names such as Quark.xxx (the reference name of the function object). Note: Each time a snapshot is taken, the GC is automatically executed first, so the objects in the view are accessible. View Interpretation column field Explanation: Constructor--Class name distance--estimate is the reference level distance from the object to the root Objects Count--gives the current number of objects of that class Shallow Size--the amount of memory the object occupies (the memory of other objects that do not contain internal references) (in bytes) Retained Size--the total memory occupied by the object (memory of other objects that contain internal references) (in bytes)
The following explains what part of the class name means: (compiled code)--unknown, estimated to be the program code area (closure)--Closure (array)--unknown Object--JS type (System)--unknown (string)--string type, sometimes a new attribute is added to the object, and the name of the property will also appear here Array--JS array type CLS--Unique inheritance class in the game Hall Window--JS's Window object Quark.displayobjectcontainer--Display container class for Quark engine Quark.imagecontainer--picture class of Quark engine Quark.text--The text class of the Quark engine Quark.togglebutton--Switch button class of Quark engine
For the CLS class name, this is due to the use of the "CLS" reference name in the game Hall inheritance mechanism, which points to the newly created inheriting class, so the objects that are instantiated by the class that use the inheritance mechanism are placed here. For example, in a program that has a class ClassA that inherits the Quark.text, the new object is placed in the CLS, not in the Quark.text.
View Object Content Click the triangle to the left of the class name to see all objects of that class. The "@70035" after the object represents the ID of the object (someone would mistakenly think of it as a memory address, the memory address will change after the GC executes, but the object ID will not). Resting the mouse over an object displays the object's internal properties and the values at that time.
This view helps us to identify which object this is. However, the view cannot be traced by who is quoted.
To view a reference relationship for an object Click on one of the objects to see the reference hierarchy of the object, such as:
object's retaining tree view shows which objects the object is referenced by, and the name of the reference. This object in the figure is referenced by 5 objects, respectively: 1. A _txtcontent variable for a CLS object; 2. A context variable for a closure function; 3. Self variable of the same closure function; 4.0 position of an array object; 5. A target variable for a Quark.tween object.
See both the context and self references, Can know that this Quark.text object uses the JS common context binding mechanism, is referenced by a variable in a closure, equivalent to the Quark.text object more than two references, this situation is more prone to memory leaks, if the closure function is not released, this Quark.text object can not be released. Expand _textcontent to see the next level of references:
To look at this tree in turn, you can see that the object (ID @70035) One of the reference chains is this: gamelistv _curv _gamelistv omit ... \ | / \ | / _noticeWidget & nbsp | _ noticec & nbsp;| _noticeV  &NBSp | & nbsp;_txtcontent | | Quark.text @70035 Comparison of memory snapshots the ability to compare snapshots allows you to know which objects were changed during the run of the program. A snapshot has just been taken, and then another shot, such as:
Click the Black Solid Circle button in the image to get a second memory snapshot:
Then click on "Snapshot 2" in the diagram to switch to the second shot of the view.
Click "Summary" in the graph to pop up a list and select the "Comparison" option, as shown in the following:
This view lists the object differences between the current view and the previous view. Column Name field Explanation: # New-How many objects have been created # Deleted--How many objects are recycled # delta--Object change value, that is, the number of new objects minus the number of objects recycled size delta-variable memory size (bytes) Note the Delta field, especially if the value is greater than 0 The object. The following is an example of Quark.tween, which expands the object as shown in:
In the "# New" column, if there is ".", it is the newly created object. In the "# Deleted" column, if there is ".", it is the object that was recycled. Usually when troubleshooting problems, you should take more than a few snapshots to compare, so it is helpful to find out the law.
Third, the memory leak troubleshooting After the memory overflow of JS program, the function body will be invalidated forever (depending on which function the JS code is running to), it usually shows that the program suddenly dies or the program appears abnormally. At this point we will be to the JS program memory leak, to find out which objects occupy the memory is not released. These objects are usually thought to be released by the developer, but in fact they are still referenced by a closure or placed in an array.
Memory leaks due to observer mode Sometimes we need to add observer mode (OBSERVER) to the program to decouple some modules, but if used improperly, it can also lead to memory leaks. To troubleshoot this type of memory leak, the main focus is on the object types being referenced are closures (closure) and array arrays. The following is a Texas hold ' em game for example:
The tester found a memory overflow problem with the Texas hold ' Em game, recreating the steps: Enter the game-exit to the partition-then into the game-and then exit to the partition, and so on several times there is a problem of game card death. The steps to troubleshoot are as follows: 1. Open the game; 2. Enter the first partition (Fast field 5/10); 3. After entering, take a snapshot of the memory; 4. Exit to the partition interface just now; 5. Enter the same partition again; 6. After entering, take the memory snapshot again; 7. Repeat steps 2 through 6 until you take 5 sets of memory snapshots; 8. Convert each group of views to the comparison comparison view; 9. Perform a memory contrast analysis. After the above steps, you can get the result:
Looking at the last snapshot first, you can see the closure (closure) +1, which is a key concern. (string), (System), and (compiled code) types can be used regardless, because there is not much information available.
Then click the second-to-last snapshot and see that the closure (closure) type is also + 1.
Then look at the last snapshot, the closure is still +1. This means that the closure function is created every time you enter the game and is not destroyed when you exit to the partition. Expand (closure), you can see very many function objects:
The number of new closures is 49, the number of closed packets recovered is 48, that is to say this operation has 48 closures correctly released, one forgot to release. Each new and reclaimed function object has a different ID, cannot find any associativity, and cannot locate which closure function is out of the question. Next open the object's retaining tree view to find out if there is an increasing array in the reference. For example, expand the reference for each function object of "Snapshot 5":
One of the function object's reference delefunc is stored in an array, the subscript is 4, and the object ID of the array is @45599. Continue to find the function object for "Snapshot 4":
It is found that there is a function reference name is also Delefunc, also stored in an array of ID @45599, subscript is 3. This object is most likely a closure that has not been released. Continue to view the function object in "Snapshot 3":
You can see the same function object, and the subscript is 2. Then there must be a memory leak problem. There is a reference name "Login_success" below the array, searching for the keyword in the program and finally locating the problematic code. Because entered the game when registered "login_success" Notice: Ob.addlistener ("login_success", _ONLOGINSUC), but exited to the partition, did not remove the notification, the next time you enter the game, and then registered again, So the function continues to increase. Change to exit to partition when the notification is removed: Ob.removelistener ("login_success", _onloginsuc), which solves the problem of this memory leak successfully. Texas hold ' em This problem is most commonly seen in the observer design pattern, where all registered notifications are stored using a global array, and if you forget to remove the notification, the array grows and eventually causes a memory overflow.
Memory leaks due to context binding Many times we will use the context binding function bind (also some people write delegate), whether it is the bind method of its own implementation or JS native bind method, there will be a memory leak hidden. Here's a simple example: <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); }, a);
B.func (); Output I am a
A = null; Release a b = null; Release B
Impersonation Context Binding function bind (func, self) { return function () { return func.apply (self); }; }; </script> In the above code, BIND holds the context self through the closure, so that the this in event B.func points to a, not B. First we put b = null; Comment out, just release a. Take a look at the memory snapshot:
We can see that there are two ClassA objects, which is inconsistent with our intention, we released a, there should only be one ClassA object B.
From the above two graphs you can see that the two objects, one is B, the other is not a, because the reference to a is already empty. The second ClassA object is the context of the closure in bind Self,self and a reference to the same object. Although A is released, but because B is not released, or B.func is not released, so that the self in the closure has been there. To release self, you can perform b=null or b.func=null. Change the code to: <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); }, a); B.func (); Output I am a A = null; Release a B.func = null; Release self Impersonation Context Binding function bind (func, self) {return function () { return func.apply (self); }; };</script> And look at the memory:
You can see that only one ClassA object B is left, and a has been released.
Iv. Conclusion The flexibility of JS is both an advantage and a disadvantage, usually write code should pay attention to the memory leak problem. When the amount of code is very large, you can't just review the code to troubleshoot the problem, there must be some monitoring and comparison tools to assist in troubleshooting. Before troubleshooting a memory leak, summarize the following common scenarios: 1. The closure context is not released after binding; 2. The Observer mode is not cleaned up in time after the notification is added; 3. The timer's processing function is not released in time, and the Clearinterval method is not called; 4. Some controls in the view layer are added repeatedly, not removed. |