为什么异常会导致Node.js中的资源泄漏?

时间:2013-04-05 04:07:00

标签: javascript node.js exception memory-management memory-leaks

如果你看一下Node.js documentation for domains的开头,它就说明了:

  

由于throw如何在JavaScript中工作,几乎从来没有任何方法可以安全地“拾取你离开的地方”,而不会泄漏引用,或者创建一些其他类型的未定义的脆弱状态。

再次在代码示例中,它在第一部分中给出了:

  

虽然我们已经阻止了突然重启过程,但我们正在泄漏疯狂的资源

我想了解为什么会这样?什么资源泄漏?他们建议您仅使用域来捕获错误并安全地关闭进程。这是所有例外的问题,而不仅仅是在使用域时?在Javascript中抛出和捕获异常是不好的做法吗?我知道这是Python中常见的模式。

修改

我可以理解为什么如果抛出异常,非垃圾收集语言中可能会出现资源泄漏,因为如果抛出异常,那么您可能运行的任何清理对象的代码都不会运行。

我可以用Javascript想象的唯一原因是抛出一个异常存储对引发异常的范围内的变量的引用(也许是调用堆栈中的东西),从而保持引用,然后保留异常对象周围,​​永远不会被清理干净。除非提到的泄漏资源是引擎内部的资源。

更新

我写了一篇博客,现在更好地解释了这个问题的答案。 Check it out

3 个答案:

答案 0 :(得分:14)

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

异常发生,这很好。接受它。关闭该过程并使用Forever之类的内容来检测它并将其设置回正轨。集群和域名也很棒。您正在阅读的文本并不是针对抛出异常的注意事项,也不是在您处理了您期望的异常时继续执行该过程 - 这是在发生意外异常时保持流程运行的警告。

答案 1 :(得分:10)

我认为当他们说“我们正在泄漏资源”时,他们的确意味着“我们可能会泄漏资源”。如果http.createServer正确处理异常,则不应泄漏线程和套接字。但是,如果它不能正确处理事情,它们当然可能是。在一般情况下,您永远不会真正知道某些事情是否始终正确处理错误。

我认为他们错误/非常误导他们说“通过...如何在JavaScript中运行如何工作,几乎没有任何方法可以安全地...... ”。关于throw如何在Javascript(与其他语言相比)中运行起来不应该是什么使得它不安全。关于throw / catch的工作方式也没有任何关系,这使得它不安全 - 除非你当然错误地使用它们。

他们应该说的是,需要妥善处理特殊情况(无论是否使用例外情况)。可以识别几种不同的类别:

一个。状态

  1. 外部状态(数据库写入,文件输出等)处于暂态状态时发生的异常
  2. 共享内存处于临时状态时发生的异常
  3. 仅局部变量可能处于瞬态的例外
  4. B中。可逆性

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

      1. 数据可以废弃
      2. 必须使用数据(即使已损坏)
      3. 无论您遇到什么类型的状态,如果您可以扭转它,您应该这样做并且您已经设置好了。问题是不可逆转的状态。如果您可以销毁损坏的数据(或将其隔离以进行单独检查),那么这是不可逆转状态的最佳举措。当抛出异常时,这会自动为局部变量完成,这就是为什么异常在处理纯功能代码中的错误(即没有可能的副作用的函数)方面表现优异的原因。同样,如果可以接受,则应删除任何共享状态或外部状态。在共享状态的情况下,要么抛出异常,直到共享状态变为本地状态,并通过展开堆栈(静态或通过GC)清理,或重新启动程序(我读过人们建议使用某些东西)像nodejitsu永远)。对于外部状态,这可能更复杂。

        最后一种情况是数据至关重要。好吧,那么你将不得不忍受你创造的错误。每个人都必须处理错误,但是当你的错误涉及到损坏的数据时,它是最糟糕的。这通常需要手动干预(重建丢失/损坏的数据,有选择地修剪等) - 在最后一种情况下,异常处理不会让你全程完成。

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

答案 2 :(得分:8)

从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);
});

在这种情况下,在关闭套接字之前handleRequest发生异常时,您正在泄漏连接。

“泄露”,因为您已完成处理请求而未事后清理。最终连接将超时并关闭套接字,但如果您的服务器处于高负载状态,它可能会在套接字用完之前耗尽。

根据您在handleRequest中的操作,您可能还会泄漏文件句柄,数据库连接,事件监听器等。

理想情况下,您应该处理异常,以便在他们之后进行清理。