js日期比较大小_node.js 内存泄漏的秘密

每日前端夜话第276篇

翻译:疯狂的技术宅

作者:Giovanny Gongora

来源:nodesource

3040a7d13d94c06013a959c6b22d0a82.gif

正文共:3955 字

预计阅读时间:10分钟

e98ac923b34c5cd11147e342cedafb08.png

一直以来,跟踪 Node.js 的内存泄漏是一个反复出现的话题,人们始终希望对其复杂性和原因了解更多。

并非所有的内存泄漏都显而易见。但是,一旦我们确定了其模式,就必须在内存使用率,内存中保存的对象和响应时间之间寻找关联。在检查对象时,应该根据自己所用的框架或技术(例如服务器端渲染),研究收集了多少对象,以及它们是否正常。希望在完成本文结束之后,你将能够理解并寻找一种策略来调试 Node.js 程序的内存消耗。

Node.js 中的垃圾回收机制

JavaScript 是一种垃圾回收语言,而 Google 的 V8 最初是为 Google Chrome 创建的JavaScript引擎,在许多情况下都可以用作独立的运行时。Node.js 中垃圾收集器的两个重要操作是:

  1. 确定有用的或无用的对象,并且

  2. 回收或重用无用对象所占用的内存。

需要记住的要点:在垃圾回收器运行时,它将完全暂停你的程序,直到完成工作为止。因此,你需要通过维护对象的引用来最大程度地减少其工作。

V8 JavaScript 引擎会自动分配和取消分配 Node.js 进程使用的所有内存。让我们看看实际情况是怎样的。

如果你将内存视为一个树结构,那么可以想象 V8 从“根节点”开始保存程序中所有的变量。这可能是你的 window 对象,也可能是 Node.js 模块中的全局对象,通常称为控制者。需要牢记的一点是,你无法对怎样取消分配“根”节点进行控制。

286ced6203fb90b40950fe3f3418df7d.png

接下来,你将找到一个 Object 节点,通常被称为叶子(没有子引用的节点)。最后 JavaScript 中有 4 种数据类型:布尔值,字符串,数字和对象。

V8 将遍历该树并尝试识别无法从“根”节点访问的数据组。如果无法从“根”节点访问该数据,则 V8 假定不再使用该数据,并释放内存。请记住:要确定某个对象是否处于活动状态,需要检查是否可通过被定义为活动对象的某个指针链到达;其他所有的情况,例如无法从根节点访问,或无法被根节点或另一个活动对象引用的对象,都会被视为垃圾。

简而言之,垃圾收集器有两个主要任务:

  1. 跟踪

  2. 计算对象之间的引用。

当你需要跟踪来自另一个进程的远程引用时,它可能会变得很棘手,但是在 Node.js 程序中,我们通常用单进程,这样使我们更加轻松。

V8 的内存方案

V8 使用类似于 Java 虚拟机的方案,并将内存划分为多个段。实现这种包装方案的东西被称为“驻留集”,它是指在 RAM 中驻留的进程所占用的内存部分。

在驻留集中,你会发现:

  • 代码段:代码实际执行的位置。

  • 栈: 包含局部变量和所有值类型,其指针引用堆上的对象或定义程序的控制流。

  • 堆: 专门用于存储引用类型(如对象、字符串和闭包)的内存段。

62ec374bc6b6ce68d1b546b87fb75195.png

还有重要的两点要记住:

  • 对象的浅大小:保存对象本身所需的内存大小

  • 对象的保留大小:当删除对象及其依赖对象时,被释放的内存大小

Node.js 有一个对象,以字节为单位描述 Node.js 进程的内存使用情况。在对象内部,你会发现:

  • rss: 是指驻留集大小。

  • heapTotal 和 heapUsed: 是指 V8 的内存使用情况。

  • external: 是指与 V8 所管理的 JavaScript 对象绑定的 C++ 对象的内存使用情况。

查找泄漏

Chrome DevTools 是一个很棒的工具,可用于通过远程调试来诊断 Node.js 程序中的内存泄漏。也有其他为你提供类似功能的工具。但是,你需要记住,概要分析是一项繁重的 CPU 任务,可能会对你的程序产生负面影响,一定要注意这一点!

我们将要介绍的 Node.js 程序是一个简单的 HTTP API Server,它具有多个端点,向使用该服务的人返回不同的信息。你可以克隆这个程序的repository。

 1const http = require('http')
2
3const leak = []
4
5function requestListener(req, res) {
6
7  if (req.url === '/now') {
8    let resp = JSON.stringify({ now: new Date() })
9    leak.push(JSON.parse(resp))
10    res.writeHead(200, { 'Content-Type': 'application/json' })
11    res.write(resp)
12    res.end()
13  } else if (req.url === '/getSushi') {
14    function importantMath() {
15      let endTime = Date.now() + (5 * 1000);
16      while (Date.now() 17        Math.random();
18      }
19    }
20
21    function theSushiTable() {
22      return new Promise(resolve => {
23        resolve('?');
24      });
25    }
26
27    async function getSushi() {
28      let sushi = await theSushiTable();
29      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
30      res.write(`Enjoy! ${sushi}`);
31      res.end()
32    }
33
34    getSushi()
35    importantMath()
36  } else {
37    res.end('Invalid request')
38  }
39}
40
41const server = http.createServer(requestListener)
42server.listen(process.env.PORT || 3000)

启动Node.js应用程序:

40d9ad334177f724afe3e4db525ab5bf.png

我们一直在使用 3S(3 Snapshot)方法进行诊断并确定可能的内存问题。有趣的是,我们发现这是 Gmail 团队的 Loreena Lee 长期使用的一种解决内存问题的方法。此方法的步骤:

  1. 打开 Chrome DevTools 并访问 chrome://inspect

  2. 在底部的“Remote Target”中,单击 inspect 按钮。

008ba8942d82d39fdff81eb8bc2c12e8.png

注意: 要确保已将 Inspector 附加到要分析的 Node.js 程序。你还可以用 ndb 连接到 Chrome DevTools。

当应用运行时,你将在控制台的输出中看到一条 Debugger Connected 消息。

  1. 转到 Chrome DevTools > Memory

  2. 获取堆快照

8f1e49dd0447766092f1a4fe25d0d5ab.png

在这种情况下,我们得到了第一个快照,而服务没有进行任何负载或处理。这是针对某些用例的提示:如果我们能够确定在接受请求或进行某些处理之前不需要对程序进行任何预热,那就很好了。有时,在获取第一个堆快照之前先进行热身操作是有意义的,因为在某些情况下,你可能会在第一次调用时对全局变量进行了延迟初始化。

  1. 在你的程序中执行你认为导致内存泄漏的操作。

在这种情况下,我们将运行  npm run load-mem。这将启动 ab 来模拟 Node.js 应用程序中的流量或负载。

eb85c8a56dec246da147a44ffe90bc2f.png

  1. 得到堆快照

1f82e8f6cef5b66ecdbcbe940245bdb3.png

  1. 再次在你的程序中执行你认为会导致内存泄漏的操作。

  2. 获取最终的堆快照

ff8bbcd10d5208fc9b772a99bdda45cc.png

  1. 选择最新得到的快照。

  2. 在窗口顶部,找到显示 “All objects” 的下拉列表,并将其切换为“Objects allocated between snapshots 1 and 2”。(如果需要,你也可以对 2 和 3 执行相同的操作)。这将大大减少你看到的对象数量。

0f9a1e48c256cf64e90b8450102f35da.png

比较视图也可以帮你识别那些对象:

ec4dd579dc9154131dfaf1cd0242c9b9.png

在该视图中,你将看到泄漏对象的列表:顶级条目(每个构造函数一行)、对象到GC根的距离、对象实例数、浅大小和保留大小。你可以通过选择一行来查看其内容。一个好的经验法则是,首先忽略括号中的项目,因为它们是内置结构。@ 字符是对象的唯一 ID,可让你比较每个对象的堆快照。

典型的内存泄漏可能是通过意外地将对对象的引用存储在无法进行垃圾回收的全局对象中,从而保留了预期仅在一个请求周期内持续存在的对象的引用。

这个例子故意留下了一个内存泄漏的问题,在请求一个从 API 查询返回的对象时生成带有日期时间戳的随机对象,并将其存储在全局数组中来泄漏该对象。通过查看几个保留的对象,你会看到一些泄漏数据的示例,可用于跟踪应用程序中的泄漏。

NSolid 非常适合这种类型的用例,因为它可以使你很好地了解在执行的每个任务或负载测试中内存是怎样增加的。如果你感到好奇,还可以实时查看每个性能分析动作如何影响 CPU。

f8346e360d2a6bfcb8abb3a7ab200b71.png

demo

在实际项目中,你不可能总是盯着用于监视程序的工具。NSolid 的一大优点是可以为应用程序的不同指标设置阈值和限制。例如,你可以将 NSolid 设置为在使用的内存量超过 X 时,或者在 X 时间内尚未从高消耗高峰恢复内存的情况下,进行堆快照。听起来不错吧?

标记和清理

V8 的垃圾收集器主要基于 Mark-Sweep 收集算法,该算法包括跟踪垃圾收集,该操作通过标记可达的对象,然后清理内存并回收未标记的对象(必须无法访问),将其纳入释放列表。这也称为世代垃圾收集器,对象可以在新声代、从新生代到老生代、以及老生代中移动。

移动对象的代价非常打,因为需要将对象的基础内存复制到新位置,并且指向这些对象的指针也需要更新。

用人话解释:

V8 递归查找所有对象到“根”节点的引用路径。例如:在 JavaScript 中,“window” 对象是可以充当 Root 的全局变量的示例。window 对象始终存在,因此垃圾收集器可以认为它及其所有子对象始终存在(即不是垃圾)。如果有任何引用,则没有指向“根”节点的路径。特别是当它以递归方式查找未引用的对象时,将被标记为垃圾,稍后将会被清除以释放该内存并将其返回给操作系统。

但是,现代的垃圾收集器以不同的方式对这种算法进行了改进,但本质是相同的:可访问的内存被标记为一类,其余的被视为垃圾。

请记住,从根可以访问到的所有内容均不视为垃圾。不需要的引用是保留在代码中某个位置的变量,这些变量将不再使用,并且指向可以释放的内存,因此,要了解 JavaScript 中最常见的泄漏,我们需要了解通常忘记引用的方式。

Orinoco 垃圾收集器

Orinoco 是最新 GC 项目的代号,它利用最新的增量和并发技术进行垃圾回收,并有释放主线程的功能。描述 Orinoco 性能的重要指标之一是垃圾回收器执行时主线程暂停的频率和时间。对于经典的“世界末日”收集者而言,这些时间间隔会因为延迟、质量差的渲染以及响应时间的增加而影响程序的用户体验。

V8 在新声代内存中的辅助流之间分配垃圾回收工作(清除)。每个流接收一组指针,然后将所有活动对象移动到“to-space”

将对象移至“to-space”时,线程需要通过读、写、比较和交换的原子操作进行同步,以避免出现另一个线程找到相同的对象但遵循不同路径并尝试移动的情况。

引用自 V8 官网:

在现有 GC 中添加并行、增量和并发技术是一项多年的努力,但已取得了回报,将大量工作移交给了后台任务。它大大改善了暂停时间、延迟和页面加载,使动画、滚动和用户交互更加顺畅。并行的 Scavenger 根据工作量将主线程新声代垃圾收集的总时间减少了大约 20%–50%。Idle-time GC 可以在 Gmail 空闲时将其 JavaScript 堆内存减少 45%。并发标记和清除可以将笨重的 WebGL 游戏中的暂停时间减少多达 50%。

Mark-Evacuate 收集器包括三个阶段:标记、复制和更新指针。为了避免在新声代中清理页面以维护空闲列表,仍然使用 semi-space 来维护新生代,它始终保持紧凑状态,即在垃圾回收期间将活动对象复制到 “to-space” 中。并行进行的好处是可以获得“exact liveness”信息。通过仅移动和重新链接主要包含活动对象的页面,可以用此信息来避免复制,这也可以由完整的 Mark-Sweep-Compact 收集器执行。它通过和标记清除算法相同的方式标记堆中的活动对象来工作,这意味着堆通常会被碎片化。V8 当前随附有并行的 Scavenger,可在大量基准测试中减少主线程新生代垃圾回收约 20%–50% 的总时间

与暂停主线程、响应时间和页面加载有关的所有方面都得到了显着改善,这使得页面上的动画、滚动和用户交互更加流畅。并行收集器可以将新内存的总处理时间减少 20–50%,具体取决于负载。但是工作还没有结束:减少停顿仍然是一项重要任务,我们将继续寻找使用更先进的技术来实现这一目标的可能性。

总结

大多数开发人员在开发 JavaScript 程序时无需考虑 GC,但是了解一些内部知识可以帮助你考虑内存使用情况和有用的编程模式。例如考虑到 V8 中基于世代的堆结构,从 GC 角度来说,维护低生存期的对象的成本实际上是相当低的,因为我们主要为存在的对象付出代价。这种模式不仅特定于 JavaScript,而且对于许多支持垃圾回收的语言也都有效。

要点:

  • 请勿使用过时或不推荐的软件包(例如,node-memwatch,node-inspector 或 v8-profiler)来检查内存。你需要的一切都已经集成在了 Node.js 的二进制文件中(尤其是 node.js 检查器和调试器)。如果你需要更专业的工具,则可以使用 NSolid、Chrome DevTools 或其他知名软件。

  • 考虑在何时何地触发堆快照和 CPU profile。由于要在生产环境中进行快照,你将会希望同时触发这两者(主要是在测试中),所以这会需要大量的 CPU 操作。另外,在关闭进程和进行冷重启之前,请确认有多少堆转储被写入了。

  • 没有哪一种工具可以解决所有问题。要根据程序的具体情况进行测试、测量、判断和解决。选择适合你体系结构的最佳工具,并选择一种可以提供更多有用数据来帮你解决问题的工具。

原文:https://nodesource.com/blog/memory-leaks-demystified

 d25a9493f4700a8d455978f7407dd960.gif

acb6eeb9f646cddf02115a138f7c34c8.png

2020年京程一灯全新课程体系即将推出,请保持关注。

愿你在新的一年里保持技术领先,有个好前程,愿你月薪30K。我们是认真的 !b1a1aef9dcdf4f194c2d99bdd60e91fc.png

✎往期精彩回顾

面向开发人员的十大 NodeJS 框架

JavaScript 类完整指南

讲给前端的正则表达式

WebAssembly 正式成为 Web 的第四种语言

2020 年 Node.js 将会有哪些新功能

2020 年 Web 开发展望

从 JavaScript、ES6、ES7 到 ES10,你学到哪儿了?

15个 Vue.js 高级面试题

8680a76494769f7243033f1256eb469b.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/258750.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win7+vs2015/13+caffe+matlab+python(CPU only)配置

首先声明本教程可以适用于vs2015 和vs2013 .以vs2015为例。 安装必备软件 vs 2015 /vs2013 matlab 2016a(64bit)推荐使用Anaconda 2.7 或者Miniconda 2.7这两个Python发布版本cmake 3.8.0 以上caffe-window: https://github.com/BVLC/caffe/tree/windows 可选软件&#xff1…

Performance Co-Pilot

Install Performance Co-Pilot 提前安装依赖 [rootiZrj97j6t7ih9hgz1me35hZ ~]# cat install.sh yum install -y docker yum install -y git yum install -y yum-utils-1.1.31-40.el7.noarch yum install lex yum install flex yum install -y bison yum install -y perl-ExtUt…

如何发布打包并发布自己的Android应用(APP)

第一步,在Eclipse中选择需要打包的项目,然后右键--选择Export,会弹出一个打包的提示框,如下图所示。 按Next之后,会继续出现一个提示框,这里你可以选择自己需要打包的项目(默认是刚才选中的&…

js变量提升_一道JS变量提升题

var a 0;if(true){a 1;function a(){};a 21;console.log(a);}console.log(a);// 21 1 当前上下文代码执行之前,会将带var/function的进行声明/定义。当遇到“{}”时,新版浏览器和老版浏览器的处理不一致。老版浏览器(IE10以下)…

Caffe训练过程:test_iter test_interval等概念

转载自http://blog.csdn.net/iamzhangzhuping/article/details/49993899 先上一张图,大家很熟悉的一张图。 首先说明一个概念:在caffe中的一次迭代iteration指的是一个batch,而不是一张图片。 下面主要说下2个概念: test_ite…

R的获取和安装

R的获取和安装 一、下载 R可以在CRAN(Comprehensive r archive network)http://cran.r-project.org上免费下载,可供选择的有Linux、Mac OS X和windows对应的二进制文件; 我这里选择的是windows版本。打开如下页面: bas…

扩展欧几里得算法求逆元_从辗转相除法到求逆元,数论算法初体验

今天是算法和数据结构专题的第22篇文章,我们一起来聊聊辗转相除法。辗转相除法又名欧几里得算法,是求最大公约数的一种算法,英文缩写是gcd。所以如果你在大牛的代码或者是书上看到gcd,要注意,这不是某某党,…

[翻译] Fast Image Cache

https://github.com/path/FastImageCache Fast Image Cache is an efficient, persistent, and—above all—fast way to store and retrieve images in your iOS application. Part of any good iOS applications user experience is fast, smooth scrolling, and Fast Image …

php练习 租房子

题目要求 1.封装类 <?php class DBDA {public $fuwuqi"localhost"; //服务器地址public $yonghuming"root";//用户名public $mima"";//密码 public $dbconnect;//连接对象//操作数据库的方法//$sql代表需要执行的SQL语句//$type代表SQL语…

centos 安装boost(caffe需要)

安装 由于安装caffe&#xff0c;要求boost的版本在1.55以上&#xff0c;而服务器上的刚好是1.54,所以进行了重装。 参考&#xff1a;《CentOS 7下编译安装Boost_1_57_0 》 不过由于pycaffe需要boost.python,因此需要在./b2时修改为./b2 –stage debug 才可以。而不能去掉py…

JAVA正则表达式介绍和使用

本文引用自 http://www.cnblogs.com/android-html5/archive/2012/06/02/2533924.html 技术博客 1.Java中在某个字符串中查询某个字符或者某个子字串 Java代码 String s "Shang Hai Hong Qiao Fei Ji Chang";    String regEx "a|F"; //表示a或F Pat…

集合框架中的接口及其实现类

Collection&#xff1a;集合层次中的根接口&#xff0c;JDK没有提供这个接口直接地实现类。Set&#xff1a;不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。List&#xff1a;是一个有序的集合&#xff0c;可以包含重复的元素。提供了按索引访问的方式。Map&#x…

C# 多线程 Parallel.For 和 For 谁的效率高?那么 Parallel.ForEach 和 ForEach 呢?

还是那句话&#xff1a;十年河东&#xff0c;十年河西&#xff0c;莫欺少年穷。 今天和大家探讨一个问题&#xff1a;Parallel.For 和 For 谁的效率高呢&#xff1f; 从CPU使用方面而言&#xff0c;Parallel.For 属于多线程范畴&#xff0c;可以开辟多个线程使用CPU内核&#x…

bigdecimal 小于等于0_图解小于 K 的两数之和

点击蓝色“五分钟学算法”关注我哟加个“星标”&#xff0c;天天中午 12:15&#xff0c;一起学算法作者 | P.yh来源 | 五分钟学算法题目描述 题目来源于 LeetCode 上第 1099 号问题&#xff1a;小于 K 的两数之和。给你一个整数数组 A 和一个整数 K&#xff0c;请在该数组中找出…

pdf 深入理解kotlin协程_Kotlin协程实现原理:挂起与恢复

今天我们来聊聊Kotlin的协程Coroutine。如果你还没有接触过协程&#xff0c;推荐你先阅读这篇入门级文章What? 你还不知道Kotlin Coroutine?如果你已经接触过协程&#xff0c;但对协程的原理存在疑惑&#xff0c;那么在阅读本篇文章之前推荐你先阅读下面的文章&#xff0c;这…

编译py-faster-rcnn的问题汇总及解决方法

按照官网 的提示&#xff0c;我开始安装faster rcnn&#xff0c;但是出现了很多问题&#xff0c;我将其汇总了起来&#xff0c;并提出了解决办法。 先说明一下我的配置&#xff1a; python : anaconda2linux: centos 6.9 安装faster rcnn请先参考&#xff1a;《cuda8cudnn4 F…

linux 安装python-opencv

三种方法&#xff1a; 1. pip 安装 &#xff1a; pip install opencv-python &#xff0c;最新版为opencv3安装后>>> import cv2 >>> print cv2.__version__参考&#xff1a;http://www.cnblogs.com/lclblack/p/6377710.html 2. anaconda的conda安装 ,可以指…

《你的灯亮着吗》读书笔记Ⅲ

转载于:https://www.cnblogs.com/yue3475975/p/4586220.html

nvidia显卡对比分析

本文章转载自&#xff1a;http://www.cnblogs.com/lijingcong/p/4958617.html 科学计算显卡的两个主要性能指标&#xff1a;1、CUDA compute capability&#xff0c;这是英伟达公司对显卡计算能力的一个衡量指标&#xff1b;2、FLOPS 每秒浮点运算次数&#xff0c;TFLOPS表示每…

零基础不建议学前端_web前端开发零基础怎样入门-哈尔滨前端学习

web前端开发零基础怎样入门-哈尔滨前端学习&#xff0c;俗话说&#xff0c;知己知彼&#xff0c;百战百胜。要想学好web前端&#xff0c;首先要了解什么是web前端&#xff0c;下面由小编来给大家介绍一下&#xff1a;1什么是web&#xff1f;Web就是在Http协议基础之上, 利用浏览…