为什么会在Node.js中导致资源泄漏?

如果您查看域的Node.js 文档的开头,它指出:

由于JavaScript在JavaScript中的工作原理,几乎没有任何方法可以安全地“捡起你离开的地方”,而不会泄漏引用或创build其他某种不明确的状态。

再次在代码示例中,它在第一部分中提到:

虽然我们已经阻止了突然重启的进程,但是我们正在疯狂地泄露资源

我想明白为什么是这样呢? 什么资源在泄漏? 他们build议您只使用域来捕获错误并安全地closures进程。 这是所有例外的问题,而不仅仅是在使用域时? 在Javascript中抛出和捕获exception是一个糟糕的做法吗? 我知道这是Python中的一种常见模式。

编辑

我可以理解,如果抛出一个exception,那么为什么在非垃圾收集语言中可能会有资源泄漏,因为如果引发exception,则可能运行的清除对象的任何代码都不会运行。

我可以想象的唯一原因是如果抛出一个exception存储引用的variables在引发exception的范围(也可能是调用堆栈中的东西),从而保持引用,然后exception对象保持周围,永远不会得到清理。 除非所引用的泄漏资源是引擎内部的资源。

UPDATE

我已经写了一个博客解释这个问题的答案现在好一点了。 一探究竟

意外的例外是你需要担心的。 如果您对应用程序的状态了解不足,无法为特定的exception添加处理,并pipe理任何必要的状态清理,那么根据定义,您的应用程序的状态是未定义的,而且是不可知的,而且很有可能挂在那不应该是。 这不只是内存泄漏,你不必担心。 未知的应用程序状态可能会导致不可预知的和不需要的应用程序行为(例如提供错误的输出 – 部分呈现的模板或不完整的计算结果,或者更糟糕的情况是每个后续输出都是错误的)。 这就是为什么当发生未处理的exception时退出进程非常重要。 它使您的应用程序有机会自行修复。

例外发生,那很好。 拥抱它。 closures这个过程并使用Forever这样的东西来检测它,并将事情重新放回正轨。 集群和领域也很棒。 您正在阅读的文本不是抛出exception的警告,或是在处理您期望的exception时继续执行的过程 – 这是在发生意外exception时保持stream程运行的警告。

我觉得当他们说“ 我们正在资源泄漏 ”时,他们真的是说“ 我们可能是在泄漏资源 ”。 如果http.createServer正确处理exception,线程和套接字不应该泄漏。 但是,如果处理不好,他们肯定会成为问题。 在一般情况下,你永远不知道是否一直处理错误。

我认为他们错误/误导他们说:“ 通过JavaScript的投掷性质,几乎没有任何方法可以安全地…… ”。 不应该有任何关于如何投掷在JavaScript(与其他语言),使其不安全。 一般来说throw / catch的作用也是不安全的 – 除非你错误地使用它们。

他们应该说的是,例外情况(无论是否使用例外情况)都需要适当处理。 有几个不同的类别可以识别:

一个状态

  1. 当外部状态(数据库写入,文件输出等)处于瞬态时发生exception
  2. 共享内存处于暂时状态时发生exception
  3. 只有局部variables可能处于瞬态的例外情况

B.可逆性

  1. 可逆/可恢复状态(例如数据库回滚)
  2. 不可逆转的状态(数据丢失,未知如何反转或禁止反转)

C.数据关键性

  1. 数据可以报废
  2. 必须使用数据(即使已损坏)

不pipe你搞乱了什么样的状态,如果你能扭转这种状态,你就应该这样做,然后你就成立了。 问题是不可逆转的状态。 如果你可以销毁损坏的数据(或隔离进行单独检查),这是不可逆转状态的最佳举措。 这是在抛出exception时为局部variables自动完成的,这就是为什么exception擅长处理纯function代码(即没有可能的副作用的函数)中的错误的原因。 同样,如果可以接受,应该删除任何共享状态或外部状态。 在共享状态的情况下,抛出exception,直到共享状态成为本地状态,并通过展开堆栈(静态或通过GC)来清除,或重新启动程序(我读过人们build议使用的东西像nodejitsu永远)。 对于外部状态,这可能更复杂。

最后一种情况是数据严重时。 那么,你将不得不忍受你创造的错误。 每个人都必须处理错误,但是当你的错误涉及损坏的数据时,它是最糟糕的。 这通常需要人工干预(重build丢失/损坏的数据,select修剪等) – 在最后一种情况下,exception处理将无法完成。

我写了一个类似的答案,涉及如何处理在一些数据存储的多个更新的情况下在各种情况下的中等操作失败: https : //stackoverflow.com/a/28355495/122422

从node.js文档中取样:

 var d = require('domain').create(); d.on('error', function(er) { // The error won't crash the process, but what it does is worse! // Though we've prevented abrupt process restarting, we are leaking // resources like crazy if this ever happens. // This is no better than process.on('uncaughtException')! console.log('error, but oh well', er.message); }); d.run(function() { require('http').createServer(function(req, res) { handleRequest(req, res); }).listen(PORT); }); 

在这种情况下,当closures套接字之前handleRequest发生exception时,您正在泄漏连接。

“泄漏”的意思是你完成处理请求后不清理。 最终,连接将超时并closures套接字,但是如果您的服务器处于高负载状态,则在发生这种情况之前可能会耗尽套接字。

根据你在handleRequest做什么,你也可能泄露文件句柄,数据库连接,事件监听器等等。

理想情况下,你应该处理你的例外,所以你可以清理后。