如何防止 node.js 中的内存泄漏?

Posted

技术标签:

【中文标题】如何防止 node.js 中的内存泄漏?【英文标题】:How to prevent memory leaks in node.js? 【发布时间】:2011-08-09 16:08:19 【问题描述】:

我们知道 node.js 为我们提供了强大的力量,但强大的力量伴随着巨大的责任。

据我所知,V8 引擎不进行任何垃圾收集。那么我们应该避免哪些最常见的错误,以确保我的节点服务器没有内存泄漏。

编辑: 抱歉我的无知,V8 确实有一个强大的垃圾收集器。

【问题讨论】:

等等,什么?没有 GC 的 JS 实现(或更一般地说,一种语言的 any 实现,其中手动内存管理脱离了程序员的手中)对我来说似乎毫无价值。事实上,谷歌向我展示了code.google.com/apis/v8/design.html#garb_coll 作为第一个结果。你从哪里得到“V8 不做 GC”的想法? V8 有一个短暂的线性垃圾收集器,可以在清理时停止世界。暗示它没有 GC 是无稽之谈。事实上,它是我们拥有的最好的 JS GC 之一。另一个很棒的是在 IE9+ 中。我听说 Mozilla 将在未来改进他们的 GC 设计,朝着 V8 发展。 【参考方案1】:

主动垃圾回收:

node --expose-gc test.js

并用于:

global.gc();

快乐编码:)

【讨论】:

手动调用垃圾收集器无助于真正的内存泄漏。无论如何,运行时都会定期调用垃圾收集器,并且 GCed 语言中的内存泄漏是由创建垃圾收集器无法安全收集的引用引起的。 但是在调试时,频繁调用 gc 可以大大提高信噪比,并且更容易判断是否存在真正的内存泄漏。【参考方案2】:

我想说服自己接受已接受的答案,具体来说:

不了解闭包如何维护对外部函数范围和上下文的引用。

因此我编写了以下代码来演示如何无法清理变量,人们可能会对此感兴趣。

如果您在另一个终端中运行watch -n 0.2 'ps -o rss $(pgrep node)',您可以看到泄漏发生。请注意使用nextTickbuffer = null 中发表评论将如何完成该过程:

(function () 
    "use strict";

    var fs = require('fs'),
        iterations = 0,

        work = function (callback) 
            var buffer = '',
                i;

            console.log('Work ' + iterations);

            for (i = 0; i < 50; i += 1) 
                buffer += fs.readFileSync('/usr/share/dict/words');
            

            iterations += 1;
            if (iterations < 100) 
                // buffer = null;

                // process.nextTick(function () 
                    work(callback);
                // );
             else 
                callback();
            
        ;

    work(function () 
        console.log('Done');
    );

());

【讨论】:

当我能以某种方式将事物形象化时,我也倾向于最好地学习、理解和接受事物;这是一个很好的例子,它让我能够做到这一点 - 看到 GC 发生时内存的变化。感谢分享。 @dukedave 我不确定这是否是一个很好的例子。 Obvioulsy 本地 buffer 变量无法在递归函数调用结束之前被垃圾收集,除非你明确地 null 它但是这种行为真的可以认为是泄漏吗?当递归调用结束时,无论您是否 null buffer,所有局部变量自然都符合 GC 条件。没有? @plalx 考虑到应用程序程序员,我认为这是一个泄漏(即他们没有意识到buffer 会增长)。也许我应该更清楚的是,这是针对已接受答案的陈述,即“您的主要问题是不了解闭包如何保持对外部函数的范围和上下文的引用”;我现在会更新我的答案以反映这一点。 用简单的例子给出很好的答案! 没有泄漏,这是一个递归调用。一个非常智能的编译器可能比你更聪明,并且只是删除缓冲区使用,因为你什么都不做:)【参考方案3】:

据我所知,V8 引擎没有 做任何垃圾收集。

V8 在构建中具有强大而智能的垃圾收集器。

您的主要问题是不了解闭包如何维护对外部函数范围和上下文的引用。这意味着您可以通过多种方式创建循环引用或创建仅被清理的变量。

这是因为你的代码是模糊的,编译器无法判断它是否是安全垃圾收集它。

强制 GC 获取数据的一种方法是清空您的变量。

function(foo, cb) 
    var bigObject = new BigObject();
    doFoo(foo).on("change", function(e) 
         if (e.type === bigObject.type) 
              cb();
              // bigObject = null;
         
    );

v8 如何知道在事件处理程序中对大对象进行垃圾收集是否安全?它没有,所以你需要通过将变量设置为 null 来告诉它不再使用它。

阅读各种文章:

http://www.ibm.com/developerworks/web/library/wa-memleak/

【讨论】:

循环引用不应该对“智能”GC 造成任何问题。即使是最简单的 GC 也可以处理它们(引用计数不是真正的 GC)。不过,关于事件处理程序的提示似乎是正确的。 是否有必要在函数范围内将“bigObject”设为空。一旦函数完成,GC不应该处理它吗? @jwerre 该功能永远不会完成。这是一个变化的监听器。只有当doFoo(foo) get 返回的事件发射器被垃圾回收时,该功能才会完成。 这真是太好了。 Raynos 说还有很多其他方法可以制造这样的问题。有人有其他需要注意的好指南吗? @Raynos 将bigObject 设置为null 在我看来在这种情况下没有任何意义。删除事件处理程序本身将允许收集bigObject,这就是我们想要的。然而,只有 nullbigObject 引用只会在下一次处理程序调用时导致运行时错误。

以上是关于如何防止 node.js 中的内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

检测代码中的 node.js/javascript 内存泄漏

第1651期如何分析 Node.js 中的内存泄漏

如何防止java中的内存泄漏

Node.js内存泄漏分析

Node.js中的内存泄漏分析

Node.js内存泄漏分析