JavaScript also talks about memory optimization

Source: Internet
Author: User
Tags tmlp

Compared with C/C ++, the processing of the JavaScript we use in memory has made us pay more attention to writing business logic in development. However, as the business continues to become more complex and the development of single-page applications, mobile HTML5 applications, and Node. js programs, the memory problems in JavaScript cause freezing and memory overflow.

This article will discuss memory usage and optimization at the JavaScript language level. From the aspects that you are familiar with or have heard of, to what you will not notice most of the time, we will analyze them one by one.

1. Language-level memory management

1.1 Scope

Scope is a very important operating mechanism in JavaScript programming. In synchronous JavaScript programming, it does not fully attract the attention of beginners, but in asynchronous programming, good scope control skills have become essential skills for JavaScript developers. In addition, the scope plays a vital role in JavaScript Memory Management.

In JavaScript, function calls, with statements, and global scopes can be formed.

Take the following code as an example:
Copy codeThe Code is as follows:
Var foo = function (){
Var local = {};
};
Foo ();
Console. log (local); // => undefined

Var bar = function (){
Local = {};
};
Bar ();
Console. log (local); // => {}

Here we define the foo () function and bar () function. Their intention is to define a variable named local. However, the final results are quite different.

In the foo () function, we use the var statement to declare and define a local variable. Because the function body has a scope, this variable is defined in this scope. In addition, the foo () function does not perform any extended scope processing. Therefore, after the function is executed, the local variable is destroyed. In the outer scope, the variable cannot be accessed.

In the bar () function, the local variable is not declared using the var statement. Instead, the local variable is directly defined as a global variable. Therefore, the variable can be accessed in the outer scope.
Copy codeThe Code is as follows:
Local = {};
// The definition here is equivalent
Global. local = {};


1.2 Scope chain

In JavaScript programming, you will certainly encounter multi-layer function nesting scenarios, which are representative of a typical scope chain.

As shown in the following code:
Copy codeThe Code is as follows:
Function foo (){
Var val = 'hello ';

Function bar (){
Function baz (){
Global. val = 'World ;'
}
Baz ();
Console. log (val); // => hello
}
Bar ();
}
Foo ();

According to the previous description about the scope, you may think that the result of the code here is world, but the actual result is hello. Many beginners will be confused here, so let's take a look at how this code works.

In JavaScript, variable identifiers are searched outward from the current scope until the global scope. Therefore, access to variables in JavaScript code can only be performed externally, but cannot be reversed.



The execution of the baz () function defines a global variable val in the global scope. When accessing the val identifier in the bar () function, according to the principle of searching from the inside out to the outside: If the val identifier is not found in the scope of the bar function, it goes to the upper layer, that is, to search for the scope of the foo () function.

However, the key to making everyone confused is here: this access to the identifier finds the correct variable in the scope of the foo () function, so it will not continue to look out, so it is in the baz () the global variable val defined in the function does not affect the variable access.

1.3 Closure

We know that the identifier search in JavaScript follows the principle from inside to outside. However, with the complexity of business logic, a single transmission order is far from meeting the increasing new demands.

Let's take a look at the following code:
Copy codeThe Code is as follows:
Function foo (){
Var local = 'hello ';
Return function (){
Return local;
};
}
Var bar = foo ();
Console. log (bar (); // => Hello

The technology shown here to allow the outer scope to access the inner scope is Closure ). Thanks to the application of higher-order functions, the scope of the foo () function is "extended 』.

The foo () function returns an anonymous function, which exists in the scope of the foo () function. Therefore, you can access the local variable within the scope of the foo () function, and save its reference. Because this function directly returns the local variable, you can directly execute the bar () function in the outer scope to obtain the local variable.

Closure is an advanced feature of JavaScript. We can use it to achieve more complex effects to meet different needs. Note that the function with internal variable reference is taken out of the external function, so the variables in the scope are not necessarily destroyed after the function is executed, until all references to internal variables are removed. Therefore, applications with closures can easily cause memory to be released.

2. Memory collection mechanism of JavaScript

Here I will use Chrome and Node. the V8 engine launched by Google is used as an example to briefly introduce the memory recycle mechanism of JavaScript. For more details, you can buy my good friend park Ling's book, "a simple introduction to Node. js, in which the "memory control" chapter has a very detailed introduction.

In V8, all JavaScript objects are allocated memory through heap.



When we declare a variable in the Code and assign a value, V8 will allocate a part of the variable to the heap memory. If the applied memory is insufficient to store this variable, V8 will continue to apply for memory until the heap size reaches the V8 memory limit. By default, the maximum heap memory size of V8 is 1464 MB in 64-bit systems, and 732 MB in 32-bit systems, that is, about 1.4 GB and 0.7 GB.

In addition, V8 manages the JavaScript objects in heap memory by generation: New Generation and old generation. A new generation is a JavaScript Object with a short life cycle, such as temporary variables and strings. The old generation is an object with a long life cycle after multiple garbage collection, such as the master controller and Server Object.

Garbage collection algorithms have always been an important part of programming language development. The garbage collection algorithms used in V8 mainly include the following:

1. Scavange algorithm: memory space management through replication, mainly used for new generation of memory space;
2. the Mark-Sweep algorithm and the Mark-Compact algorithm organize and recycle the heap memory by marking. They are mainly used to check and recycle the old generation objects.


PS: More detailed V8 garbage collection implementation can be learned by reading relevant books, documents and source code.

Let's take a look at under what circumstances the JavaScript engine will recycle objects.

2.1 Scope and reference

Beginners often mistakenly believe that when the function is executed, the objects declared inside the function will be destroyed. However, this understanding is not rigorous and comprehensive, and can be easily confused.

Reference is a very important mechanism in JavaScript programming, But it is strange that General developers do not pay attention to it or even understand it. Reference refers to the abstract relationship of "code access to objects", which is similar to the pointer of C/C ++, but not the same. References are also the most critical mechanism of the JavaScript engine in garbage collection.

The following code is used as an example:
Copy codeThe Code is as follows:
//......
Var val = 'Hello world ';
Function foo (){
Return function (){
Return val;
};
}
Global. bar = foo ();
//......

After reading this code, can you tell me which objects are still alive after the code is executed?

According to relevant principles, objects not released in this Code are val and bar (). What makes them unable to be recycled?

How does the JavaScript engine collect garbage? The garbage collection algorithm mentioned above is only used for garbage collection. How does it know which objects can be recycled and which objects need to survive? The answer is a reference to a JavaScript Object.

In JavaScript code, even if you simply write a variable name as a single row without any operation, the JavaScript engine will regard it as an access behavior to the object, and there is a reference to the object. To ensure that garbage collection does not affect the running of the program logic, the JavaScript engine must not recycle the currently used objects. Otherwise, it will be messed up. Therefore, the criterion used to determine whether an object is in use is whether the object is still referenced. But in fact, this is a compromise. Because JavaScript references can be transferred, some references may be brought to the global scope, but in fact, there is no need to access it in the business logic, and it should be recycled, but the JavaScript engine still holds that the program still needs it.

How to Use variables and references in the correct posture is the key to optimizing JavaScript at the language level.

3. Optimize Your JavaScript

Finally, I am very grateful to you for your patience. After so many introductions, I believe you have a good understanding of the JavaScript Memory Management mechanism, the following tips will make you feel better.

3.1 make good use of functions

If you have the habit of reading excellent JavaScript projects, you will find that many Daniel often uses an anonymous function to wrap the code at the outermost layer when developing the front-end JavaScript code.
Copy codeThe Code is as follows:
(Function (){
// Main business code
})();
Some are even more advanced:
Copy codeThe Code is as follows:
; (Function (win, doc, $, undefined ){
// Main business code
}) (Window, document, jQuery );
Even front-end modular loading solutions such as RequireJS, SeaJS, and OzJS all adopt similar forms:
Copy codeThe Code is as follows:
// RequireJS
Define (['jquery '], function ($ ){
// Main business code
});

// SeaJS
Define ('m odule', ['dep', 'underscore '], function ($ ,_){
// Main business code
});
If you say that the Code of many Node. js open-source projects is not handled in this way, you will be wrong. Before running the code, Node. js packs each. js file into the following format:
Copy codeThe Code is as follows:
(Function (exports, require, module, _ dirname, _ filename ){
// Main business code
});

What are the benefits of doing so? We all know that at the beginning of this article, JavaScript can form function calls, with statements, and global scopes. We also know that objects defined in the global scope will probably survive until the process exits. If they are large objects, it will be troublesome. For example, some people like to make template rendering in JavaScript:
Copy codeThe Code is as follows:
<? Php
$ Db = mysqli_connect (server, user, password, 'myapp ');
$ Topics = mysqli_query ($ db, "SELECT * FROM topics ;");
?>
<! Doctype html>
<Html lang = "en">
<Head>
<Meta charset = "UTF-8">
<Title> Are you a monkey? </Title>
</Head>
<Body>
<Ul id = "topics"> </ul>
<Script type = "text/tmpl" id = "topic-tmpl">
<Li>
<H1> <% = title %> <P> <% = content %> </p>
</Li>
</Script>
<Script type = "text/javascript">
Var data = <? Php echo json_encode ($ topics) ;?>;
Var topicTmpl = document. querySelector ('# topic-tmpl'). innerHTML;
Var render = function (tmlp, view ){
Var complied = tmlp
. Replace (/\ n/g, '\ n ')
. Replace (/<% = ([\ s \ S] + ?) %>/G, function (match, code ){
Return '"+ escape (' + code + ') + "';
});

Complied = [
'Var res = "";',
'With (view | {}){',
'Res = "'+ complied + '";',
'}',
'Return res ;'
]. Join ('\ n ');

Var fn = new Function ('view', complied );
Return fn (view );
};

Var topics = document. querySelector ('# topics ');
Function init ()
Data. forEach (function (topic ){
Topics. innerHTML + = render (topicTmpl, topic );
});
}
Init ();
</Script>
</Body>
</Html>

This kind of code is often seen in the work of new users. What are the problems here? If the amount of data obtained from the database is very large, the data variable will be idle after the template rendering is completed on the front end. Because this variable is defined in the global scope, the JavaScript engine will not recycle and destroy it. This variable will remain in the old generation heap memory until the page is closed.

However, if we make some very simple changes, we can outer a layer of functions in the logic code, so the effect will be different. After the UI rendering is complete, the code reference data is removed. when the execution of the outermost function is complete, the JavaScript engine starts to check the objects in the code, data can also be recycled.

3.2 do not define global variables

As we mentioned earlier, when a variable is defined in a global scope, the JavaScript engine will not recycle and destroy it by default. This variable will remain in the old generation heap memory until the page is closed.

We will always follow the principle: Never use global variables. Although global variables are quite easy to develop, the problems caused by global variables are far more serious than the convenience they bring.

Make it difficult to recycle variables;
1. obfuscation occurs when multiple users collaborate;
2. the scope chain is vulnerable to interference.
3. With the preceding packaging function, we can also use the packaging function to process global variables 』.

3.3 manually remove variable reference

If a variable is no longer needed in the Business Code, You can manually remove the variable reference to recycle it.
Copy codeThe Code is as follows:
Var data = {/* some big data */};
// Blah
Data = null;

3.4 make good use of callback

In addition to using closures for internal variable access, we can also use very popular callback functions for business processing.
Copy codeThe Code is as follows:
Function getData (callback ){
Var data = 'some big data ';

Callback (null, data );
}

GetData (function (err, data ){
Console. log (data );

A callback function is a subsequent transmission Style (CPS) technology. This Style of programming transfers the business focus of the function from the return value to the callback function. Besides, it has many advantages over closures:

1. If the input parameter is of the basic type (such as string or value), the input parameter in the callback function will be a copy value. After the Business Code is used, it is easier to recycle it;
2. Through callback, in addition to synchronous requests, we can also use asynchronous programming, which is a very popular writing style;
3. The callback function itself is usually a temporary anonymous function. Once the function is requested to be executed, the reference of the callback function itself will be removed and recycled.

3.5 good closure Management

When we must use closures for business needs (such as loop event binding, private attributes, and callback with parameters), please be cautious with the details.

Loop binding events are a required course for getting started with JavaScript closures. Assume that there are six buttons corresponding to six events. When a user clicks the button, output the corresponding event in the specified place.


Copy codeThe Code is as follows:
Var btns = document. querySelectorAll ('. btn'); // 6 elements
Var output = document. querySelector ('# output ');
Var events = [1, 2, 3, 4, 5, 6];

// Case 1
For (var I = 0; I <btns. length; I ++ ){
Btns [I]. onclick = function (evt ){
Output. innerText + = 'clicked' + events [I];
};
}

// Case 2
For (var I = 0; I <btns. length; I ++ ){
Btns [I]. onclick = (function (index ){
Return function (evt ){
Output. innerText + = 'clicked' + events [index];
};
}) (I );
}

// Case 3
For (var I = 0; I <btns. length; I ++ ){
Btns [I]. onclick = (function (event ){
Return function (evt ){
Output. innerText + = 'clicked' + event;
};
}) (Events [I]);
}

The first solution here is obviously a typical loop binding event error, which is not described here. For details, refer to the answer I gave to a netizen; the difference between the second and third solutions lies in the parameters passed in by the closure.

In the second scheme, the input parameter is the current loop subscript, while the latter is the direct input of the corresponding event object. In fact, the latter is more suitable for a large amount of data applications, because in JavaScript functional programming, parameters passed in during function calls are basic type objects, the shape parameter obtained in the function body is a copy value, which is defined as a local variable in the scope of the function body, after binding the event, you can manually unreference the events variable to reduce memory usage in the outer scope. When an element is deleted, corresponding event listening functions, event objects, and closure functions are destroyed and recycled.

3.6 memory is not cache

Caching plays an important role in business development and can reduce the burden on time and space resources. However, it should be noted that the memory should not be used as a cache easily. Memory is nothing for any program development. If it is not a very important resource, do not directly put it in the memory, or create an expiration mechanism to automatically destroy the expired cache.

4. Check the memory usage of JavaScript

During normal development, we can also use some tools to analyze and troubleshoot memory usage in JavaScript.

4.1 Blink/Webkit Browser

In the Blink/Webkit browser (Chrome, Safari, Opera etc.), we can use the Profiles tool of Developer Tools to check the memory of our program.


4.2 Node. js memory check

In Node. js, we can use the node-heapdump and node-memwatch modules to perform Memory checks.
Copy codeThe Code is as follows:
Var heapdump = require ('heapdump ');
Var fs = require ('fs ');
Var path = require ('path ');
Fs. writeFileSync (path. join (_ dirname, 'app. Pi'), process. pid );
//...
Copy codeThe Code is as follows: <span style = "font-family: Georgia, 'times New Roman ', 'bitstream Charter', Times, serif; font-size: 14px; line-height: 1.5em; "> after node-heapdump is introduced in the Business Code, we need. the js process sends a SIGUSR2 signal, allowing node-heapdump to capture a snapshot of heap memory. </Span>
Copy codeThe Code is as follows: $ kill-USR2 (cat app. pid)

In this way, there will be a file directory with heapdump-<sec>. <usec>. the Snapshot file named in heapsnapshot format can be opened and checked using the Profiles tool in Developer Tools in the browser.

5. Summary

Soon after the end of the article, this article mainly shows you the following:

1. JavaScript is closely related to memory usage at the language level;
2. memory management and recovery mechanisms in JavaScript;
3. How to Use the memory more efficiently so that the generated JavaScript can be more dynamic and scalable;
4. How to check the memory in case of memory problems.

I hope that through the study of this article, you can produce better JavaScript code, so that mom can feel at ease and reassure the boss.

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.