【问题标题】:Do I have a memory leak? (nodejs)我有内存泄漏吗? (节点)
【发布时间】:2020-01-03 02:31:10
【问题描述】:

我有一个在服务器上运行的用 nodejs 编写的应用程序。我注意到(通过不时手动检查服务器负载)我的应用程序从大约 50MB 的总分配 RAM 开始(其中大约 10 MB 是 HEAP)但 6 天后它总共使用了 100 MB(尽管HEAP 或多或少保持不变)。

首先,我尝试使用 Chrome 对其进行调试,但调试器仅监控已分配 RAM 的 HEAP 部分(我没有注意到那里有任何问题)。另一方面,我在有限的时间内观察了调试器。

所以,为了进一步监控 RAM 使用情况,我在我的应用程序中编写了以下代码

var maxRecordedRam = 0;
ramCheck = setInterval(() => {
    let ram = process.memoryUsage();
    let ramRss = Math.round(ram.rss / 1024 / 1024 * 100) / 100;
    let ramHeap = Math.round(ram.heapUsed / 1024 / 1024 * 100) / 100;
    if ( maxRecordedRam < ramRss ) {
        // memory load is higher then the last recorded value
        maxRecordedRam = ramRss;
        modulUtile.log(
                "RAM: " + ramRss + " MB (Heap: " + ramHeap + " MB)", 
                "INFO"
        );
    }
}, 30000);

请注意modulUtile.log() 方法是console.log 的一种包装器,用于将消息记录到文件中。

6 天后,我在日志文件中有以下输出(文件较大,我只选择了一些值):

[2019-08-23 07:10:19]    RAM: 44.27 MB (Heap: 10.03 MB)
.....
[2019-08-23 07:25:19]    RAM: 56.81 MB (Heap: 11.57 MB)
.....
[2019-08-23 09:13:19]    RAM: 65.85 MB (Heap: 15.72 MB)
.....
[2019-08-23 14:47:49]    RAM: 90.97 MB (Heap: 19.14 MB)
.....
[2019-08-25 09:49:52]    RAM: 93.9 MB (Heap: 10.66 MB)
.....
[2019-08-29 12:39:30]    RAM: 97.02 MB (Heap: 17.23 MB)

HEAP 分配在工作时间最多为 20MB,在夜间和早上(当应用处于空闲状态时)约为 10MB。但是,虽然 HEAP 上下波动,但总 RAM 只是在上升。

我的问题:是否有可能在HEAP区域之外发生内存泄漏(因为据我所知,只有HEAP区域用于存储变量)?

【问题讨论】:

    标签: node.js debugging memory-leaks


    【解决方案1】:

    堆内存不可能“泄漏”到堆栈内存中。

    还可以打印出 memoryUsage() 的 heapTotal 和外部值。

    如果 heapTotal 占了大部分空闲内存,那么 JVM 正在为您的程序提供越来越多的堆,而不会将其交还给系统。这不是您的代码中的内存泄漏,当未使用的内存变得足够大时,内存可能会返回给系统。

    如果内存的外部部分在增长,那么某些非托管 .dll 正在消耗越来越多的内存,这可能是由于其中的内存泄漏,或者可能是由于线程管理不善。调试外部库,尤其是那些您无法控制的库,可能会很痛苦。

    如果这些都没有增长,那么您自己的代码堆栈正在增长。这可能是由两件事引起的:重复和线程管理不善。

    您的代码使用的线程数是否在增加?然后线程在某个时候挂起,并且新的线程正在产生。每个线程都包含少量堆栈,在线程正确退出其生命周期之前不会返回。

    除非明显有可能,否则重复不应成为罪魁祸首,例如,某些函数可以通过函数调用而不是返回循环回到主工作流程。

    JVM 也有可能没有正确跟踪已使用的堆,尽管不太可能。在 JVM 中检测此类错误的方法是在不同的 JVM 上运行程序并查看数字是否大致相同,或者新的 JVM 是否开始报告更大的 heapUsed 值。

    如果没有更多信息,我首先会怀疑“JVM 保留未使用的内存以避免释放和重新获取它”,然后是“外部 .dll 存在内存泄漏”。

    【讨论】:

      【解决方案2】:

      所以基本上堆内存是作为 RAM 的一部分分配的,它更多是硬件内存组件,而堆是由特定服务或一组服务分配或创建的逻辑组件,在这种情况下,您的 NodeJS服务。 现在,在您的情况下,很可能是垃圾收集的 pos 堆内存正在被清除,但是由于您的 RAM 已经在 RAM 中为该内存分配了空间,并且考虑到使用量并不多,即。 2Gb 左右中的 100mb 更少。所以系统不认为它应该把这个换出哪个硬盘或任何其他可用的内存存储。

      同样如this article 中所述,JS V8 引擎分配内存并将其分为 A Stack 和 Heap 内存分配。本文将解释哪些不同类型的操作会导致堆栈或堆内存增加。

      鉴于上述情况,我鼓励您对代码进行压力测试,以使其达到内存利用率峰值,此时您的堆和 RAM 使用量超过设备或 VM 等可用总 RAM 的 80%。

      垃圾收集吞吐量之间存在固有的权衡, 延迟和内存消耗。例如,垃圾收集 延迟(导致用户可见的卡顿)可以通过使用更多来减少 内存以避免频繁的垃圾收集调用。为了 低内存移动设备,即 RAM 低于 512 MB 的设备, 将延迟和吞吐量优先于内存消耗可能会导致 在 Android 上的内存不足崩溃和暂停的选项卡中。

      以上代码来自this brilliant article,,它表示可以通过优先考虑 GC 或代码执行延迟来部分控制内存分配和释放。这意味着您可以优先考虑何时要销毁数组或对象或将它们设置为空,或者您希望处理请求的速度和允许的并发性,或者应该存储在内存中并同时处理的少量数据,从而减少延迟。

      总而言之,虽然堆内存泄漏可能会导致 RAM 使用率上升,但不会以相互为代价,因为堆是 RAM 的一部分。因此,由于堆栈内存或其他可能与您的 NodeJS 服务相关或不相关的相关服务,RAM 使用量也可能会增加。但是,一旦您可以执行多个压力测试并对其进行基准测试并自己分析结果,所有这些都会变得显而易见。因为行为非常特定于您的应用程序代码以及代码的结构。

      关于可视化,当您对 NodeJS 应用程序进行压力测试时,您可以使用this tool 来分析您的代码执行情况,上面提到的文章还简要介绍了注意事项。

      【讨论】:

      • 您好,感谢您的回答。我已经阅读了这些文章,但我仍然不知道是否可能在堆外发生内存泄漏(原始问题)。我发现的所有调试工具都只适用于内存的堆部分(包括你建议的那个),我在那里没有问题,因为堆会随着服务器负载而波动。
      • 我认为这就是文章和答案(最后几段)试图解释的内容,内存泄漏在概念上实际上并不是从虚拟内存空间泄漏到另一个内存空间(如堆栈或你的机箱内存。因此,假设您的 RAM 使用量是 100Mb,其中包括 20Mb 的堆内存,而另外 80Mb 是您的 NodeJS 堆栈内存和系统上运行的其他服务。因此,RAM中可能的内存增加可能是由其他服务引起的。检查您是否使用htop。参考 - askubuntu.com/a/138685 --- 或 --- 简而言之 - 没有内存泄漏不会像那样工作。
      • 我知道内存泄漏不是从一个虚拟空间转移到另一个。当软件继续创建元素而不取消引用它们(并且不允许 GC 释放内存)时,就会发生泄漏。这是我的问题,因为据我所知,这些元素仅在 HEAP 区域内创建。关于htop,这就是我首先注意到问题的方式(正如我在问题中所述)。内存不能被系统上的其他服务使用,process.memoryUsage()只报告实际运行进程的使用情况。
      • 这是否意味着堆栈内存正在使用它......因为您的堆是恒定的,并且以某种方式或其他方式您的堆栈内存正在增加......
      • 这是对的。如果 HEAP 不是问题,那么堆栈区域可能会出现问题。但是,这甚至可能吗?因为我发现的所有示例都只引用了堆区域。甚至 Chrome 的内存调试工具也只是监控堆部分。或多或少,我的问题是这样的:堆区域外是否可能存在内存泄漏?或者仅仅是操作系统没有清空内存(不知何故......),因为其他进程不需要额外的内存。
      猜你喜欢
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      相关资源
      最近更新 更多