Node.js和碎片

背景 :我来自微软的世界,在这个世界里,我曾经把网站存储在IIS上。 经验教会我每天回收我的应用程序池一次,以消除由于碎片造成的奇怪问题。 回收应用程序池基本上意味着重新启动您的应用程序,而无需重新启动整个IIS。 我还观看了一个演讲,解释了微软如何在.Net 4.5中减less了很多碎片。

现在,我正在将Node.js应用程序部署到生产环境中,并且必须确保它始终完美地工作。 我原本以为使我的应用程序每天重新启动一次。 然后我做了一些研究,以便在Node.js中find关于碎片问题的一些线索。 我发现的唯一的东西是V8中描述GC的文章中的一小段 :

为了确保快速的对象分配,短暂的垃圾收集暂停,“无内存碎片V8”采用了世界末日,准确的垃圾收集器。

这个声明对我来说真的不足以让我放弃构build我的应用程序的重启机制,但另一方面,如果没有问题,我不想做一些工作。

所以我的问题是:

我应该或不应该重新启动我的应用程序,以防止碎片?

在知道内存消耗确实是一个问题之前实现服务器重启是一个过早的优化。 因此,我认为你不应该这样做,直到你真的发现这是一个问题。 与内存消耗相比,您可能会发现更多优化问题。

要确定是否需要重新启动服务器,我build议您执行以下操作:

  1. 设置一些监控工具,如https://newrelic.com/ ,让你的监控你的performance。
  2. 不断监视你的记忆。 尝试查看内存消耗量是否稳定增长,或者是否平稳增长。
  3. 在需要采取行动之前,先决定可接受的阈值。 例如,一旦您的应用程序消耗了60%的系统内存,您需要开始考虑重新启动服务器,并决定重新启动时间间隔。
  4. 确定在重新启动服务器时是否有“停机时间”。 如果你不想停机,你可能需要build立一个代理层来引导stream量。

一般来说,我build议服务器重新启动所有dynamic垃圾收集语言。 这在大型应用程序中相当普遍。 在你的代码库或你所依赖的某个库中的一个小错误几乎是不可避免的,它会泄漏内存。 即使你修好了一个漏洞,你最终还是会得到一个。 这可能会使您的团队感到沮丧,基本上会导致服务器重新启动策略,并定义您的应用程序在内存消耗方面可以接受的内容。

我同意@Parris。 你可能应该弄清楚你是否真的需要首先有一个重启策略。 我build议在这里使用pm2 文档 。 即使你不想注册keymetrics,它是一个相当不错的小stream程pipe理者,而且真正快速设置。 您可以从命令行获取内存使用情况的报告。 看起来像这样。

pm2输出

另外,如果以上述方式启动集群模式,则可以调用pm2 restart my_app ,第一个可能会在最后一个脱机之前pm2 restart my_app (这是一个额外的好处,有8个进程的真正原因是利用全部8个核心)。 如果您坚持停机时间,您可以根据ID重新启动它们。

我赞同@Parris,这似乎是一个不成熟的优化。 而且,重新启动并不是解决潜在问题的方法,这是对症状的一种治疗。

如果内存错误对于您的节点应用程序来说是一个普遍的问题,那么我认为,为什么在您的程序中首先出现这种碎片可能是一个有价值的努力。 了解为什么程序长时间运行后出现内存错误,并重构程序的体系结构以解决问题的根源,在我看来,这不是解决症状的更好的解决scheme。

我相信两件事情会使你受益。

  1. 不可变的对象将有很大的帮助,它们比使用可变对象更具可预测性,并且不会受到项目生命周期的影响。 另外,因为不可变对象是只读内存块,所以它们比服务器必须花费资源决定是否读取或写入存储对象的内存块上的可变对象更快。 我目前使用名为IMMUTABLE的库,它适用于我。 还有其他的,就像Deep Freeze一样 ,但是我从来没有用过它。

  2. 确保正确地pipe理应用程序的进程,内存泄漏是我遇到的这个问题的第二大贡献者。 再次,这是通过考虑你的应用程序是如何构造的,以及如何处理用户事件来解决的,确保一旦进程没有被客户机使用,从heap适当地移除它,如果不是那么heap保持直到所有的内存消耗导致应用程序崩溃(参考下图看V8的内存Scheme,以及heap位置)。 Node是一个C ++程序,它由Google的V8和Javascript控制。

V8内存方案

您可以使用Node.js的process.memoryUsage()来监视内存使用情况。 当你确定如何pipe理你的堆V8提供了两个解决scheme,一个是清除,这是非常快,但不完整。 另一个是马克 – 扫描,这是缓慢的,并释放所有未被引用的内存。

关于如何pipe理你的堆以及如何pipe理运行Node.js的V8的内存,请参考这篇博文

因此,对您的实施负责的方法是密切关注开放的stream程,深入了解heap ,以及如何释放未引用的内存块。 考虑到这一点来创build项目也会使项目更具可扩展性。