转载自: http://blog.eood.cn/node-js_gc
最近做的服务器端组件大部分都在使用 Node.js 。因为 Node.js 库管理模式比较先进,并且依托于 Github 的流行,Node.js 开源的库非常多,一般所需要的第三方库都可以找到。虽然这些库有很多明显的 Bug 但是比从零自己开发要快很多。对于服务器端开发,Node.js 还是个不错的选择,不像 Erlang 更接近底层,业务层面的库相对要少很多。
- 查看 Node.js 进程的 GC log
- 关于 Node.js 的 GC
- Dump 出 heap 的内容到 Chrome 分析
- Node.js 调试工具 node-inspector
- Node.js 命令行调试工具
- Node.js 其他常用命令参数
- Node.js 的 Profiling
- 参考以及一些有用的链接
最近写的一个功能在本地开发的时候没有明显问题,但是到真实环境测试的时候发现内存不断增长,并且增长很快,同时 CPU 占用也很高,接近单核心的 100% 。这对于一个大部分都是 IO 操作的进程显然是有问题的。所以尝试分析内存和 CPU 异常的原因。最终发现是因为生产者和消费者速度差异引起的缓冲区暴增。在 MySQL 连接对象的 Queue 中积压了大量的 Query,而不是内存泄漏。
查看 Node.js 进程的 GC log:
node --trace_gc --trace_gc_verbose test.js
61 ms: Scavenge 2.2 (36.0) -> 1.9 (37.0) MB, 1 ms [Runtime::PerformGC]. Memory allocator, used: 38780928, available: 1496334336 New space, used: 257976, available: 790600 Old pointers, used: 1556224, available: 0, waste: 0 Old data space, used: 1223776, available: 4768, waste: 0 Code space, used: 1019904, available: 0, waste: 0 Map space, used: 131072, available: 0, waste: 0 Cell space, used: 98304, available: 0, waste: 0 Large object space, used: 0, available: 1495269120 97 ms: Mark-sweep 11.7 (46.1) -> 5.4 (40.7) MB, 10 ms [Runtime::PerformGC] [GC in old space requested]. Memory allocator, used: 42717184, available: 1492398080 New space, used: 0, available: 1048576 Old pointers, used: 1390648, available: 165576, waste: 0 Old data space, used: 1225920, available: 2624, waste: 0 Code space, used: 518432, available: 501472, waste: 0 Map space, used: 60144, available: 70928, waste: 0 Cell space, used: 23840, available: 74464, waste: 0 Large object space, used: 3935872, available: 1491332864
关于 Node.js 的 GC
Node.js 的 GC 方式为分代 GC (Generational GC)。对象的生命周期由它的大小决定。对象首先进入占用空间很少的 new space (8MB)。大部分对象会很快失效,会频繁而且快速执行 Young GC (scavenging)*直接*回收这些少量内存。假如有些对象在一段时间内不能被回收,则进入 old space (64-128KB chunks of 8KB pages)。这个区域则执行不频繁的 Old GC/Full GC (mark-sweep, compact or not),并且耗时比较长。(Node.js 的 GC 有两类:Young GC: 频繁的小量的回收;Old GC: 长时间存在的数据)
Node.js 最新增量 GC 方式虽然不能降低总的 GC 时间,但是避免了过大的停顿,一般大停顿也限制在了几十 ms 。
为了减少 Full GC 的停顿,可以限制 new space 的大小
--max-new-space-size=1024 (单位为 KB)
手动在代码中操作 GC (不推荐)
node --expose-gc test.js
修改 Node.js 默认 heap 大小
node --max-old-space-size=2048 test.js (单位为 MB)
Dump 出 heap 的内容到 Chrome 分析:
安装库
https://github.com/bnoordhuis/node-heapdump
在应用的开始位置添加
var heapdump = require('heapdump');
在进程运行一小段时间后执行:
kill -USR2 <pid>
这时候就会在当前目录下生成 heapdump-xxxxxxx.heapsnapshoot 文件。
将这个文件 Down 下来,打开 Chrome 开发者工具中的 Profiles,将这个文件加载进去,就可以看到当前 Node.js heap 中的内容了。
可以看到有很多 MySQL 的 Query 堆积在处理队列中。内存暴涨的原因应该是 MySQL 的处理速度过慢,而 Query 产生速度过快。
所以解决方式很简单,降低 Query 的产生速度。内存暴涨还会引起 GC 持续执行,占用了大量 CPU 资源。
node-mysql 库中的相关代码,其实应该限制 _queue 的 size,size 过大则抛出异常或者阻塞,就不会将错误扩大。
Protocol.prototype._enqueue = function(sequence) { if (!this._validateEnqueue(sequence)) { return sequence; } this._queue.push(sequence); var self = this; sequence .on('error', function(err) { self._delegateError(err, sequence); }) .on('packet', function(packet) { self._emitPacket(packet); }) .on('end', function() { self._dequeue(); }); if (this._queue.length === 1) { this._parser.resetPacketNumber(); sequence.start(); } return sequence; };
在不修改 node-mysql 的情况下,加入生产者和消费者的同步,调整之后,内存不再增长,一直保持在不到 100M 左右,CPU 也降低到 10% 左右。
Node.js 调试工具 node-inspector
安装:
npm install -g node-inspector
启动自己的程序:
node --debug test.js node --debug-brk test.js (在代码第一行加断点)
启动调试器界面:
node-inspector
打开 http://localhost:8080/debug?port=5858 可以看到执行到第一行的断点。
右边为局部变量和全局变量、调用栈和常见的断点调试按钮,查看程序步进执行情况。并且你可以修改正在执行的代码,比如在关键的位置增加 console.log 打印信息。
Node.js 命令行调试工具
以 DEBUG 模式启动 Node.js 程序,类似于 GDB:
node debug test.js debug> help Commands: run (r), cont (c), next (n), step (s), out (o), backtrace (bt), setBreakpoint (sb), clearBreakpoint (cb), watch, unwatch, watchers, repl, restart, kill, list, scripts, breakOnException, breakpoints, version
Node.js 其他常用命令参数
node --max-stack-size 设置栈大小 node --v8-options 打印 V8 相关命令 node --trace-opt test.js node --trace-bailout test.js 查找不能被优化的函数,重写 node --trace-deopt test.js 查找不能优化的函数
Node.js 的 Profiling
V8 自带的 prof 功能:
npm install profiler node --prof test.js
会在当前文件夹下生成 v8.log
安装 v8.log 转换工具
sudo npm install tick -g
在当前目录下执行
node-tick-processor v8.log
可以关注其中 Javascript 各个函数的消耗和 GC 部分
[JavaScript]: ticks total nonlib name 67 18.7% 20.1% LazyCompile: *makeF /opt/data/app/test/test.js:6 62 17.3% 18.6% Function: ~ /opt/data/app/test/test.js:9 42 11.7% 12.6% Stub: FastNewClosureStub 38 10.6% 11.4% LazyCompile: * /opt/data/app/test/test.js:1 [GC]: ticks total nonlib name 27 7.5%
参考以及一些有用的链接
http://cs.au.dk/~jmi/VM/GC.pdf
http://lifecs.likai.org/2010/02/how-generational-garbage-collector.html
http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Na.C3.AFve_mark-and-sweep
http://en.wikipedia.org/wiki/Cheney’s_algorithm
https://github.com/bnoordhuis/node-heapdump
http://mrale.ph/blog/2011/12/18/v8-optimization-checklist.html
http://es5.github.com/
http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/
https://hacks.mozilla.org/2013/01/building-a-node-js-server-that-wont-melt-a-node-js-holiday-season-part-5/
https://gist.github.com/2000999
http://www.jiangmiao.org/blog/2247.html
http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/
http://blog.caustik.com/2012/04/11/escape-the-1-4gb-v8-heap-limit-in-node-js/
https://developers.google.com/v8/embed#handles
https://hacks.mozilla.org/2012/11/fully-loaded-node-a-node-js-holiday-season-part-2/
https://code.google.com/p/v8/wiki/V8Profiler
相关推荐
总之,《Node.js调试指南》涵盖了Node.js开发中的各种调试技术,从基础到高级,从同步到异步,从错误处理到性能分析,全面地指导开发者提升调试技能,从而编写更稳定、更高效的Node.js应用。通过学习和实践这些技巧...
Full Stack Javascript - Learn Backbone.js, Node.js and MongoDB (APress 2015).epub Learning Node.js for Mobile Application Development (Packt 2015).pdf Microsoft Press Node.js for .NET Developers (2015...
Node.js 是一个基于 JavaScript 的服务器端运行平台,允许开发者使用 JavaScript 语言来编写服务器端应用程序。 Node.js 的出现使得 JavaScript 成为服务器端脚本语言。 Node.js 的主要特点是非阻塞 I/O 和事件驱动...
Node.js 是一个开源、跨平台的 JavaScript 运行环境,它允许开发者在服务器端执行 JavaScript 代码。Node.js 使用 V8 引擎,这是 Google 为 Chrome 浏览器开发的高性能 JavaScript 和 WebAssembly 引擎。Node.js 的...
Node.js 应用程序是用 JavaScript 编写的,可以在 Mac OS X、Windows 和 Linux 上的 Node.js 运行时中运行而无需更改。 Node.js 应用程序旨在最大限度地提高吞吐量和效率,使用非阻塞 I/O 和异步事件。Node.js 应用...
Node.js 是一个开源、跨平台的 JavaScript 运行环境,它让开发者可以在服务器端执行 JavaScript 代码。Node.js 使用了 Google V8 引擎,这个引擎是为 Chrome 浏览器设计的,因此 Node.js 具有高性能和高效性的特点。...
标题中的“Node.js-Node.js for Mobile Apps”指的是将Node.js环境移植到移动设备上,使得开发者能够在Android和iOS平台上直接运行Node.js应用程序。这一技术的出现极大地拓展了JavaScript的适用范围,不再局限于Web...
Node.js,作为一个基于Chrome V8引擎的JavaScript运行环境,自其诞生以来,就以其高效、异步非阻塞I/O以及事件驱动的特性,迅速在Web开发领域崭露头角。本资料将带你深入探索Node.js的核心思想,并剖析其背后的libuv...
Node.js 应用程序是用 JavaScript 编写的,可以在 Mac OS X、Windows 和 Linux 上的 Node.js 运行时中运行而无需更改。 Node.js 应用程序旨在最大限度地提高吞吐量和效率,使用非阻塞 I/O 和异步事件。Node.js 应用...
讲解可能包括了JavaScript在Node.js中的异步非阻塞I/O模型,事件驱动编程的概念,以及如何安装和设置Node.js环境。 2. **模块系统**:Node.js拥有强大的模块系统,如内置模块(如fs、http)和第三方模块(如Express...
Node.js的核心特性包括单线程事件循环、异步编程、模块系统以及强大的文件系统操作等。在本书中,你将学习如何利用这些特性来开发高效的应用程序。 1. **事件驱动编程**:Node.js采用事件驱动模型,当有I/O操作时,...
Node.js 是一个开源、跨平台的JavaScript运行环境,它允许开发者在服务器端执行JavaScript代码,极大地拓宽了JavaScript的应用领域。标题“Welcome to Node.js v14.17.6”表明我们将探讨的是Node.js的特定版本——v...
6. **性能优化**:探讨Node.js的性能监控、调试技巧以及优化策略,如异步编程、内存管理等。 7. **部署与维护**:讲解如何将Node.js应用部署到云平台,如Heroku、Docker,以及持续集成和持续部署(CI/CD)的实践。 8...
Node.js 是一个开源、跨平台的JavaScript运行环境,它允许开发者在服务器端执行JavaScript代码,极大地拓宽了JavaScript的应用领域。10.16.3-x64 版本是 Node.js 的一个稳定版本,适用于64位操作系统。下面将详细...
在Node.js开发中,调试是不可或缺的一环,它有助于我们找出代码中的错误和性能瓶颈。`node-inspector`是一个强大的工具,它允许我们使用Web Inspector(Chrome或Safari浏览器的开发者工具)来调试Node.js应用。这篇...
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许开发者在服务器端使用 JavaScript 进行编程。Node.js 提供了一个丰富的生态系统,包含了大量的开源库和工具,使得开发网络应用变得更加便捷。在...
8. **性能监控与调试**:了解如何使用工具如New Relic、PM2进行性能监控,以及使用Node.js内置的debug模块进行调试。 9. **错误处理**:良好的错误处理是保证程序稳定运行的关键。书中会介绍如何有效地捕获和处理...
Node.js是一种开源、跨平台的JavaScript运行环境,它允许开发者在服务器端运行JavaScript代码,极大地扩展了JavaScript的应用范围。Node.js基于Chrome V8引擎,因此它具有高性能和高效率的特点。这个压缩包文件包含...
Node.js 应用程序是用 JavaScript 编写的,可以在 Mac OS X、Windows 和 Linux 上的 Node.js 运行时中运行而无需更改。 Node.js 应用程序旨在最大限度地提高吞吐量和效率,使用非阻塞 I/O 和异步事件。Node.js 应用...
Node.js 是一个开源的、跨平台的 JavaScript 运行环境,它允许开发者在服务器端运行 JavaScript 代码。Node.js 的强大之处在于它提供了一个丰富的生态系统,包含了大量的第三方模块,其中 `node-inspector` 就是其中...