磁盘索引节点和内存索引节点_节点js中的内存模型内存泄漏

磁盘索引节点和内存索引节点

I n this post, using examples, we will learn about Garbage Collection, memory management, and memory leaks in Node.JS.

在这篇文章中,通过示例,我们将学习有关垃圾收集,内存管理和Node.JS中的内存泄漏的信息。

Let’s start.

开始吧。

总览 (Overview)

JavaScript automatically allocates memory when objects are created and frees it when they are not used anymore (garbage collection). This automatic process is a potential source of confusion: it can give developers the false impression that they don’t need to worry about memory management.

JavaScript在创建对象时自动分配内存,并在不再使用对象时将其释放(垃圾回收)。 这种自动过程可能会引起混乱:它会给开发人员一种错误的印象,即他们不必担心内存管理。

记忆模型 (Memory Model)

堆:(Heap:)

This is where the V8 Engine stores objects or dynamic data. This is the biggest block of memory area and this is where Garbage Collection(GC) takes place. The entire heap memory is not garbage collected, only the New and Old spaces are managed by garbage collection. Memory allocation is randomly placed. To prevent memory “holes” in the Heap the V8 engine has memory managers that prevent that from occurring. The Heap is further divided into the below:

V8引擎在这里存储对象或动态数据。 这是最大的内存区域,这是垃圾回收(GC)发生的地方。 整个堆内存不是垃圾回收,只有新空间和旧空间由垃圾回收管理。 内存分配是随机放置的。 为了防止堆中的内存“空洞”,V8引擎提供了内存管理器来防止这种情况的发生。 堆进一步分为以下几类:

  • New Space: This is where new objects live, and most of these objects are short-lived. The New Space is small and designed to be GC’ed (“Scavenge Algorithm”) very quickly, independent of other spaces. New Space size can be controlled using the --min_semi_space_size(Request) and --max_semi_space_size(Limit) V8 flags.

    新空间:这是新对象居住的地方,其中大多数是短暂的。 新空间很小,并且可以快速进行GC(“清除算法”),而与其他空间无关。 可以使用--min_semi_space_size (请求)和--max_semi_space_size (限制)V8标志来控制新的空间大小。

  • Old Space: This is where objects that survived the “New space” for two GC cycles are moved to. This space is managed up by GC (Mark-Sweep & Mark-Compact algorithm) The size of Old Space can be controlled using the --initial_old_space_size(Request) and --max_old_space_size(Limit) V8 flags. The Old Space is divided to:

    旧空间:这是将在两个GC周期中在“新空间”中幸存的对象移动到的地方。 此空间由GC(标记扫描和标记紧凑算法)管理 可以使用--initial_old_space_size (Request)和--max_old_space_size (Limit)V8标志来控制旧空间的大小。 旧空间分为:

    Old Space: This is where objects that survived the “New space” for two GC cycles are moved to. This space is managed up by GC (Mark-Sweep & Mark-Compact algorithm) The size of Old Space can be controlled using the --initial_old_space_size(Request) and --max_old_space_size(Limit) V8 flags. The Old Space is divided to:Old Pointer Space: Contains most objects which may have pointers to other objects.

    旧空间:这是将在两个GC周期中在“新空间”中幸存的对象移动到的地方。 此空间由GC(标记扫描和标记紧凑算法)管理 可以使用--initial_old_space_size (Request)和--max_old_space_size (Limit)V8标志来控制旧空间的大小。 旧空间分为:旧指针空间:包含大多数可能具有指向其他对象的指针的对象。

    Old Space: This is where objects that survived the “New space” for two GC cycles are moved to. This space is managed up by GC (Mark-Sweep & Mark-Compact algorithm) The size of Old Space can be controlled using the --initial_old_space_size(Request) and --max_old_space_size(Limit) V8 flags. The Old Space is divided to:Old Pointer Space: Contains most objects which may have pointers to other objects.Old Data Space: Contains objects which just contain raw data without pointers to other objects.

    旧空间:这是将在两个GC周期中在“新空间”中幸存的对象移动到的地方。 此空间由GC(标记扫描和标记紧凑算法)管理 可以使用--initial_old_space_size (Request)和--max_old_space_size (Limit)V8标志来控制旧空间的大小。 旧空间分为:旧指针空间:包含大多数可能具有指向其他对象的指针的对象。 旧数据空间:包含仅包含原始数据而没有指向其他对象的指针的对象。

  • Large object space: This is where objects which are larger than the size limits of other spaces live. Each object gets its own mmap'd region of memory. Large objects are never moved by the garbage collector.

    较大的对象空间:这是大于其他空间大小限制的对象所在的地方。 每个对象都有自己的mmap'd内存区域。 大对象永远不会被垃圾收集器移动。

  • Code-space: This is where the Just In Time(JIT) compiler stores compiled code Blocks. This is the only space with executable memory (although Codes may be allocated in “Large object space”, and those are executable, too).

    代码空间即时(JIT)编译器此处存储已编译的代码块。 这是唯一具有可执行内存的空间(尽管可以在“大对象空间”中分配Codes ,并且这些代码也是可执行的)。

  • Cell space, property cell space, and map space: These spaces contain Cells, PropertyCells, and Maps, respectively. Each of these spaces contains objects which are all the same size and has some constraints on what kind of objects they point to, which simplifies collection.

    单元空间,属性单元空间和地图空间:这些空间分别包含CellsPropertyCellsMaps 。 这些空间中的每个空间都包含相同大小的对象,并且对它们指向的对象有某种约束,从而简化了收集。

Each of these spaces is composed of a set of pages. A Page is a contiguous chunk of memory allocated from the operating system with mmap or mapviewoffile(Windows). Each page is 1MB in size, except for Large object space.

这些空间中的每一个都由一组页面组成。 页面是使用mmap或mapviewoffile(Windows)从操作系统分配的连续内存块。 除较大的对象空间外,每个页面的大小均为1MB。

堆栈: (Stack:)

There is one stack per V8 process. This is where static data including method/function frames, primitive values, and pointers to objects are stored. The variables are stored in a LIFO method - the last one in is the first one out.The stack memory limit can be set using the --stack_size V8 flag.

每个V8进程只有一个堆栈。 在此存储包括方法/功能框,原始值和对象指针的静态数据。 变量存储在LIFO方法中-最后一个输入是第一个输出。可以使用--stack_size V8标志设置堆栈内存限制。

例: (Example:)

class Dog {}
const dog = new Dog()
let person = {
name: "John doe"
}

In this example we can see that dog and person are reference types, their values are stored in the Heap and they are pushed to the stack with the value of the memory address of the location in Heap.

在此示例中,我们可以看到dog和person是引用类型,它们的值存储在Heap中,并将它们与Heap中位置的内存地址的值一起推入堆栈。

扫频算法 (Mark-and-sweep algorithm)

This algorithm reduces the definition of “an object is no longer needed” to “an object is unreachable”.

该算法将“不再需要一个对象”的定义简化为“一个无法访问的对象”。

The algorithm starts from the root of the application. For the browser, the root is the window, and for Node.js it is the global object.

该算法从应用程序的根目录开始。 对于浏览器,根是窗口,对于Node.js,它是全局对象。

Using this algorithm, the GC will identify the reachable and unreachable objects. All the unreachable objects will be automatically garbage collected.

使用此算法,GC将识别可到达和不可到达的对象。 所有无法访问的对象将被自动垃圾收集。

As of 2012, all modern browsers ship a mark-and-sweep GC.

自2012年起,所有现代浏览器都附带标记扫掠GC

Side Note: As of 2020, it is no possible to explicitly or programmatically trigger garbage collection in JavaScript.

旁注:截至2020年,不可能在JavaScript中显式或以编程方式触发垃圾回收。

内存泄漏 (Memory Leaks)

JavaScript memory leaks are caused by invalid logical flow in the code.

JavaScript内存泄漏是由代码中无效的逻辑流引起的。

A Memory leak can be defined as a piece of memory that is no longer being used or required by an application but for some reason is not returned back to the OS and is still being occupied needlessly.

内存泄漏可以定义为不再由应用程序使用或需要的内存,但由于某种原因不会泄漏回操作系统,并且仍被不必要地占用。

Let’s see some real-life examples for memory leaks:

让我们来看一些有关内存泄漏的真实示例:

全局变量 (Global Variables)

Global variables by definition are not swept away by GC. It is important to remember to use global variables carefully and never forget to either null it or reassign it after their use.

根据定义,全局变量不会被GC清除。 重要的是要记住要小心使用全局变量,不要忘记在使用全局变量后将其清空或重新分配。

范围 (Scope)

The current context of execution. The context in which values and expressions are “visible” or can be referenced. If a variable or other expression is not “in the current scope,” then it is unavailable for use.

当前执行上下文。 值和表达式“可见”或可以引用的上下文。 如果一个 变量或其他表达式不在“当前范围内”,则无法使用。

Memory leak in JS often occurs due to scoping rules. A reference to an undeclared variable creates a new variable inside the global object. In the case of browsers, the global object is window.

JS中的内存泄漏通常是由于作用域规则而发生的。 对未声明变量的引用会在全局对象内创建一个新变量。 对于浏览器,全局对象是window。

Node.js Example:

Node.js示例:

function leak() {
text = "leak";
}

In this example after the leak function called, the text still exists in the global scope variable. That happened because of Hoisting and that can lead to a memory leak.

在此示例中,在调用泄漏函数之后,文本仍存在于全局范围变量中。 发生这种情况是由于吊装,可能导致内存泄漏。

How to solve this issue: simply use the keyword var/const/let. When we use those keywords the variable will be created inside the scope of the function’s/object’s, and when the scope is cleared the GC will collect it.

如何解决此问题:只需使用关键字var/const/let. 当我们使用这些关键字时,变量将在函数/对象的作用域内创建,而当清除作用域时, GC将收集该变量。

关闭 (Closures)

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)

闭包是捆绑在一起(封闭)的函数及其周围状态(词汇)的组合。 环境)

Example:

例:

function outer() {
var data = "Javascript";
function inner() {
console.log(data);
}
return inner;
}

var func = outer();
func();

In this example, we can see that the inner function uses the data of the outer function. What happens when the parent (outer) scope of closure is removed?!

在此示例中,我们可以看到内部函数使用外部函数的数据。 删除父(外部)关闭范围会发生什么?!

The closure holds a copy of it in memory. Because of this behavior, the callers will still refer to the old variables from the closure’s parent scope.

闭包将其副本保存在内存中。 由于这种行为,调用者仍将引用闭包的父作用域中的旧变量。

计时器和事件 (Timers & Events)

The use of setTimeout, setInterval, Observers and event listeners can cause memory leaks when heavy object references are kept in their callbacks without proper handling.

如果在没有适当处理的情况下将重对象引用保留在其回调中,则使用setTimeout,setInterval,Observer和事件侦听器可能会导致内存泄漏。

Let’s review some examples:

让我们回顾一些例子:

Interval

间隔

var data = {
text: "data"
};
setInterval(function() {
var stringData = JSON.
console.log(stringData);
}, 1000);

In this example, we can see another potential memory leak. Let’s understand why. The object represented by data may be removed in the future, which will make the whole block inside the interval handler unnecessary. The interval is still active and for that reason cannot be garbage collected and its dependencies cannot be collected either. That means that data, cannot be collected.

在此示例中,我们可以看到另一个潜在的内存泄漏。 让我们了解为什么。 由data表示的对象将来可能会被删除,这将使间隔处理程序内的整个块不再需要。 该间隔仍处于活动状态,因此无法进行垃圾回收,也无法收集其依赖项。 这意味着, data ,无法收集。

How to solve this issue: simply use the clearInterval method:

如何解决此问题:只需使用clearInterval方法:

var data = {
text: "data"
};
var interval = setInterval(function() {
var stringData = JSON.
console.log(stringData);
}, 1000);
clearInterval(interval);

Now the interval is clear and its dependencies will be cleared as well.

现在,该间隔已清除,并且其依存关系也将被清除。

Timeout

超时

The handler of the timeout will stay active and in memory even after the timeout happened. Because the handler stays active his dependencies cannot be collected either.

即使发生timeout的处理程序也将保持活动状态并保留在内存中。 由于处理程序保持活动状态,因此也无法收集其依赖项。

How to solve this issue: simply use the clearTimeout method.

如何解决此问题:只需使用clearTimeout方法。

概要 (Summary)

Let’s summarize what we’ve learned in this post:

让我们总结一下我们在这篇文章中学到的知识:

  • Memory management

    内存管理
  • Garbage Collection

    垃圾收集
  • Memory leaks with real-life examples.

    带有实际示例的内存泄漏。

Bonus (: Mozilla has published a nice article about how to find memory leaks in Node.js.

奖励(: Mozilla发表了一篇不错的文章,关于如何在Node.js中查找内存泄漏

Thank you for your time, hope you enjoyed this!

谢谢您的时间,希望您喜欢这个!

翻译自: https://medium.com/@galbatz/memory-model-memory-leaks-in-node-js-395d63b8ba11

磁盘索引节点和内存索引节点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值