Home > Web Front-end > JS Tutorial > body text

Detailed explanation of memory management in JavaScript

青灯夜游
Release: 2021-01-06 10:14:35
forward
2138 people have browsed it

Detailed explanation of memory management in JavaScript

Related recommendations: "javascript video tutorial"

Most of the time, we only develop without understanding the knowledge about memory management. Because the JS engine will handle this for us. However, sometimes we encounter problems such as memory leaks. Only by knowing how memory allocation works can we solve these problems.

In this article, we mainly introduce the working principles of memory allocation and garbage collection and how to avoid some common memory leaks problems.

Cache (Memory) Life Cycle

In JS, when we create a variable, function or any object, the JS engine allocates memory for it and releases it when it is no longer needed.

Allocate memory is the process of reserving space in memory, while Release memory releases space to prepare it for other purposes.

Every time we allocate a variable or create a function, the storage of that variable goes through the same stages:

Detailed explanation of memory management in JavaScript

Allocating Memory

  • JS handles this for us: it allocates the memory we need to create the object.

Using memory

  • Using memory is something we do explicitly in the code: reading and writing memory is actually reading and writing variables. Read and write.

Release memory

  • This step is also handled by the JS engine, once the allocated memory is released, it can be used for new purposes.

"Object" in the context of memory management includes not only JS objects, but also functions and function scopes.

Memory Heap and Stack

Now we know that for everything we define in JS, the engine allocates memory and frees it when the memory is no longer needed.

The next question that comes to my mind is: Where will these things be stored?

The JS engine can store data in two places: Memory heap and Stack. Heap and stack are two data structures used by the engine for different purposes.

Stack: static memory allocation

Detailed explanation of memory management in JavaScript

The stack is a data structure used by JS to store static data. Static data is data whose size the engine knows at compile time. In JS, includes primitive values ​​pointing to objects and functions (strings, number, boolean, undefined, and null) and reference types.

Since the engine knows that the size will not change, it will allocate a fixed amount of memory for each value.

The process of allocating memory immediately before execution is called static memory allocation. The limits of these values ​​and the entire stack are browser dependent.

Heap: Dynamic Memory Allocation

Heap is another space for storing data, where JS stores objects and functions.

Unlike the stack, the JS engine does not allocate a fixed amount of memory for these objects, but allocates space as needed. This way of allocating memory is also called Dynamic Memory Allocation.

The characteristics of these two stores will be compared below:

Stack Heap
Storage basic types and references
The size is known at compile time
Allocate a fixed amount of memory
Objects and functions
The size is not known until runtime
No restrictions

Examples

Let’s take a few examples to enhance the image.

const person = {
  name: 'John',
  age: 24,
};
Copy after login
Copy after login

JS allocates memory for this object in the heap. The actual values ​​are still the original values, that's why they are stored on the stack.

const hobbies = ['hiking', 'reading'];
Copy after login

Arrays are also objects, that's why they are stored in the heap.

let name = 'John'; // 为字符串分配内存
const age = 24; // 为字分配内存

name = 'John Doe'; // 为新字符串分配内存
const firstName = name.slice(0,4); // 为新字符串分配内存
Copy after login

The initial value is immutable, so JS does not change the original value, but creates a new value.

References in JavaScript

All variables first point to the stack. If it is a non-primitive value, the stack contains a reference to the object in the heap .

Heap memory is not sorted in a specific way, so we need to keep a reference to it on the stack. We can think of references as addresses and the objects in the heap as the houses to which those addresses belong.

Remember that JS stores objects and functions on the heap. Primitive types and references are stored on the stack.

Detailed explanation of memory management in JavaScript

In this photo, we can observe how different values ​​are stored. Notice how person and newPerson both point to the same object.

Example

const person = {
  name: 'John',
  age: 24,
};
Copy after login
Copy after login

This will create a new object in the heap and create a reference to the object on the stack.

Garbage Collection

Now, we know how JS allocates memory for various objects, but in the memory life cycle, there is one last step: Release memory.

Just like memory allocation, the JavaScript engine also handles this step for us. More specifically, the Garbage Collector is responsible for this job.

Once the JS engine recognizes that a variable or function is no longer needed, it will release the memory it occupied.

The main problem with this is that whether some memory is still needed is an undecidable question, which means that it is impossible to have an algorithm that can immediately collect all the memory that is no longer needed the moment it is no longer needed.

Some algorithms can solve this problem well. I'll discuss the most common methods in this section: Reference counting and Mark clearing algorithms.

Reference counting

When a variable is declared and a reference type value is assigned to the variable, the number of references to this value is 1. If the same value is assigned to another variable, the number of references to the value is increased by 1. On the contrary, if the variable containing a reference to this value obtains another value, the number of references to this value is reduced by 1.

When the number of references to this value becomes 0, it means that there is no way to access this value anymore, so the memory space it occupies can be reclaimed. This way, the next time the garbage collector runs, it will free the memory occupied by values ​​with zero references.

Let’s look at the example below.

Detailed explanation of memory management in JavaScript

Please note that in the last frame, only hobbies are left in the heap, because the last reference is the object.

Number of Cycles

Reference CountingThe problem with the algorithm is that it does not account for circular references. This happens when one or more objects refer to each other but they can no longer be accessed through code.

let son = {
  name: 'John',
};

let dad = {
  name: 'Johnson',
}

son.dad = dad;
dad.son = son;

son = null;
dad = null;
Copy after login

Detailed explanation of memory management in JavaScript

Since the parent objects refer to each other, the algorithm does not release the allocated memory and we can no longer access both objects.

Setting them to null will not cause the reference counting algorithm to recognize that they are no longer used since they all have incoming references.

Mark and clear

The mark and clear algorithm has solutions for cyclic dependencies. It detects whether they are accessible from the root object instead of simply calculating the reference to the given object.

The browser's root is the window object, while the root in NodeJS is global.

Detailed explanation of memory management in JavaScript

This algorithm marks unreachable objects as garbage and then scans (collects) them. The root object will never be collected.

This way, circular dependencies are no longer a problem. In the previous example, neither the dad object nor the son object is accessible from the root. Therefore, they will all be marked as garbage and collected.

This algorithm has been implemented in all modern browsers since 2012. Only performance and implementation have been improved, but the core idea of ​​the algorithm remains the same.

tradeoff

Automatic garbage collection allows us to focus on building applications instead of wasting time on memory management. However, there are trade-offs.

Memory Usage

Because the algorithm does not know exactly when memory is no longer needed, a JS application may use more memory than it actually needs.

Even if an object is marked as garbage, it is up to the garbage collector to decide when and if the allocated memory will be collected.

如果你希望应用程序尽可能提高内存效率,那么最好使用低级语言。 但是请记住,这需要权衡取舍。

性能

收集垃圾的算法通常会定期运行以清理未使用的对象。

问题是我们开发人员不知道何时会回收。 收集大量垃圾或频繁收集垃圾可能会影响性能。然而,用户或开发人员通常不会注意到这种影响。

内存泄漏

在全局变量中存储数据,最常见内存问题可能是内存泄漏

在浏览器的 JS 中,如果省略varconstlet,则变量会被加到window对象中。

users = getUsers();
Copy after login

在严格模式下可以避免这种情况。

除了意外地将变量添加到根目录之外,在许多情况下,我们需要这样来使用全局变量,但是一旦不需要时,要记得手动的把它释放了。

释放它很简单,把 null 给它就行了。

window.users = null;
Copy after login

被遗忘的计时器和回调

忘记计时器和回调可以使我们的应用程序的内存使用量增加。 特别是在单页应用程序(SPA)中,在动态添加事件侦听器和回调时必须小心。

被遗忘的计时器

const object = {};
const intervalId = setInterval(function() {
  // 这里使用的所有东西都无法收集直到清除`setInterval`
  doSomething(object);
}, 2000);
Copy after login

上面的代码每2秒运行一次该函数。 如果我们的项目中有这样的代码,很有可能不需要一直运行它。

只要setInterval没有被取消,则其中的引用对象就不会被垃圾回收。

确保在不再需要时清除它。

clearInterval(intervalId);
Copy after login

被遗忘的回调

假设我们向按钮添加了onclick侦听器,之后该按钮将被删除。旧的浏览器无法收集侦听器,但是如今,这不再是问题。

不过,当我们不再需要事件侦听器时,删除它们仍然是一个好的做法。

const element = document.getElementById('button');
const onClick = () => alert('hi');

element.addEventListener('click', onClick);

element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
Copy after login

脱离DOM引用

内存泄漏与前面的内存泄漏类似:它发生在用 JS 存储DOM元素时。

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item) => {
    document.body.removeChild(document.getElementById(item.id))
  });
}
Copy after login

删除这些元素时,我们还需要确保也从数组中删除该元素。否则,将无法收集这些DOM元素。

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item, index) => {
    document.body.removeChild(document.getElementById(item.id));
    elements.splice(index, 1);
  });
}
Copy after login

由于每个DOM元素也保留对其父节点的引用,因此可以防止垃圾收集器收集元素的父元素和子元素。

总结

在本文中,我们总结了 JS 中内存管理的核心概念。写这篇文章可以帮助我们理清一些我们不完全理解的概念。

希望这篇对你有所帮助,我们下期再见,记得三连哦!

原文地址:https://felixgerschau.com/javascript-memory-management/

作者:Ahmad shaded

译文地址:https://segmentfault.com/a/1190000037651993

更多编程相关知识,请访问:编程入门!!

The above is the detailed content of Detailed explanation of memory management in JavaScript. For more information, please follow other related articles on the PHP Chinese website!

source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!