JavaScript memory mechanism

JavaScript memory space is not a concept often referred to, can easily be overlooked. Including myself, but I found them because the blur perception, resulting in a lot of things have to understand not understand. Why such basic reference data types are called reference data types? Shallow copy and deep copy What is the difference? There closures, prototype, and so on. Learn JavaScript memory mechanism helps developers can clearly recognize what they write code that occurs during execution of before, but also can improve the code quality of the project.

 

JS memory space is divided into a stack, heap, pool (also generally classified as stack).

In which the stack for variables, stack storage complex object, a constant pool of storage, it is also called constant pool.

Stack data structure

Stack is a list of special elements in the stack can only be accessed by an end of a list, called the end of the stack. After a stack is referred to as first-out data structure. Since the stack has a rear first-out features, so anyone who is not top of the stack elements are inaccessible. In order to obtain an element bottom of the stack, you must first remove the above elements.

 

Like the image above table tennis, table tennis topmost No. 5, which was the last to be put in, but you can be the first to use, but if we want to use the bottom of the table tennis No. 1, we have to be on top of the 4 tennis taken out, it can be used.

 

Heap data structure

Stacks are sorted through the tree data structure, each node has a value. Heap access is free, just as we take books on library shelves, although placing the book is in order, but we want to take any do not like, like when a stack, first remove all the previous books, No. we only need to care about the name of the book or books, you can be taken to a book you want.

The relationship between variable types and memory

Basic data types

There are six kinds of basic data types:

  1. Sting

  2. Number

  3. Boolean

  4. null

  5. undefined

  6. Symbol (ES6 introduces a new type of raw data Symbol, represent a unique value which is the seventh JavaScript language data types are interested to know:.. Https: //www.jianshu.com/p/f40a77bbd74e)

Basic data types stored in the stack memory because the basic data types footprint small, fixed size, by value accessed through, belonging to frequently used data. However, the closure base data type variable is not stored in the stack memory, but stored in the heap memory.

years num1 = 1; 
let num2 = 1;

 

Reference data types

Array, Function, Object ... can be considered in addition to the basic data types mentioned above, all types are reference data types.

Reference data type is stored in the heap memory, as reference data type occupies a large space, the size is not fixed. If the storage in the stack, will affect the performance of running; refers to a type of data stored in the stack pointer, the pointer to the start address of the stack entity. When the interpreter to find reference value, it will first retrieve its address on the stack, obtained from the heap after obtaining the address of the entity.

// basic data types - stack memory 
   let a1 = 0; 
// basic data types - stack memory 
   let a2 = 'this is string'; 
// basic data types - stack memory 
   let a3 = null; 
// pointer to the object stored in the stack memory pointer to the object stored in the heap memory 
   let b = { m: 20 }; 
// pointer stored in the stack memory array, the array pointer is stored in the heap memory 
   let c = [1, 2, 3];

So when we want to access reference data types heap memory, in fact, we first address pointer is acquired from the object variable, and then get the data we need from the heap.

 

More storage concepts clear, then it may well explain some common problems:

 

 

The 1.ES6 const can be "reassigned"

ES6Syntax  const to declare a read-only constant. Once declared, the value of the constant can not be changed. However, the following code can change  const the value of:

 

const foo = {}; foo.prop = 123; foo.prop // 123

foo = {}; // Identifier 'foo' has already been declared

 

This is because the foo stored in memory accounts, the stored value is always an object of address, change the object value when there is no impact on the value of the foo stored.

2. Copy the variables

Basic data type of replication

let a = 20; 
let b = a; 
b = 30; 
console.log(a); // 此时a的值是多少,是30?还是20?

答案是:20

在这个例子中,a、b 都是基本类型,它们的值是存储在栈内存中的,a、b 分别有各自独立的栈空间, 所以修改了 b 的值以后,a 的值并不会发生变化。

从下图可以清晰的看到变量是如何复制并修改的。

引用数据类型的复制

let m = { a: 10, b: 20 }; 
let n = m; 
n.a = 15; 
console.log(m.a) //此时m.a的值是多少,是10?还是15? 

答案是:15

在这个例子中,m、n都是引用类型,栈内存中存放地址指向堆内存中的对象, 引用类型的复制会为新的变量自动分配一个新的值保存在变量中, 但只是引用类型的一个地址指针而已,实际指向的是同一个对象, 所以修改 n.a 的值后,相应的 m.a 也就发生了改变。

 

内栈内存和堆内存的垃圾回收

有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"。栈内存中变量一般在它的当前执行环境结束就会被销毁被垃圾回收制回收, 而堆内存中的变量则不会,因为不确定其他的地方是不是还有一些对它的引用。 堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。

内存泄露

什么是内存泄漏?对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 不再用到的内存,没有及时释放,就叫做内存泄漏。 

 

闭包的内存泄漏问题

闭包中的变量并不保存在栈内存中,而是保存在堆内存中。 这也就解释了函数调用之后为什么闭包还能引用到函数内的变量。

function A() {   
    let a = 1;   
    function B() {       
        console.log(a);   
    }   
    return B; 
let res = A();

函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。

函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的,所以函数B依旧能引用到函数A中的变量。

原因就在于A是B的父函数,而B被赋给了一个全局变量res,这导致B始终在内存中,而B的存在依赖于A,因此A也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

 

那么垃圾回收机制是怎样的呢?

以下是JavaScript垃圾回收机制中如何判断内存中已经不再使用了的两种算法:

1.引用计数算法

引用其实就是指向某一物体的指针,也可简单将引用视为一个对象访问另一个对象的路径。

引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需了。

// 创建一个对象person,他有两个指向属性age和name的引用 
var person = {age: 12, name: 'aaaa'}; 
// 虽然设置为null,但因为person对象还有指向name的引用,因此name不会回收 
person.name = null; 
var p = person; 
//原来的person对象被赋值为1,但因为有新引用p指向原person对象,因此它不会被回收  
person = 1;  
//原person对象已经没有引用,很快会被回收
p = null;

2.标记清除算法

现代浏览器通用的大多是基于标记清除算法的某些改进算法,和引用记数算法总体思想都是一致的。

标记清除算法将“不再使用的对象”定义为“无法达到的对象”。

简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

从这个概念可以看出,无法触及的对象包含了没有引用的对象这个概念(没有任何引用的对象也是无法触及的对象)。但反之未必成立。

email.message = document.createElement(“div”); 
displayList.appendChild(email.message); 
displayList.removeAllChildren();

div元素已经从DOM树中清除,也就是说从DOM树的根部无法触及该div元素了。但是请注意,div元素同时也绑定了email对象。所以只要email对象还存在,该div元素将一直保存在内存中。

总结

虽然JavaScript提供了垃圾回收机制,但是还是会存在内存泄漏的问题。如果你的引用只包含少量JS交互,那么内存管理不会对你造成太多困扰。但是如果你的引用包含大量JS交互,那么就应当在写代码时候多考虑内存泄露的问题了。不要满足于写出能运行的程序,也不要认为机器的升级就能解决问题,要从根源上解决问题还得从代码开始。

 

 

参考:

1.https://juejin.im/post/5b10ba336fb9a01e66164346#heading-14

2.https://juejin.im/post/5d116a9df265da1bb47d717b

3.http://www.ruanyifeng.com/blog/2017/04/memory-leak.html

发布了16 篇原创文章 · 获赞 39 · 访问量 3万+

Guess you like

Origin blog.csdn.net/weixin_39357177/article/details/99710392