繁体   English   中英

奇怪的 Nodejs 内存泄漏

[英]Strange Nodejs memory leak

嘿,我在 heroku 中托管的 nodejs 服务器上遇到了奇怪的内存泄漏。 我试图找到泄漏两天,在我一项一项删除我的功能后,我发现这是导致问题的原因,但我仍然不确定问题到底出在哪里。 我可以清楚地看到我从 heroku 指标中出现了内存泄漏。 在此处输入图片说明 有人知道为什么这个 func 会产生内存泄漏。 谢谢

completeTicTacToeGame: function (game, winnerIndex, reasonForFinish, userId) {
    if (userId) {
      User.findOne({
        _id: userId
      }, function (err, user) {

        if (err || user == null) {
        }
        else {
          userLivesController.removeUserHeartWithoutResponeAndSaving(user, 'ticTacToeHearts', function (user, canPlay) {
            if (canPlay) {
              dateFormatterController.checkIfDateIsToday(user.lastCompitedTicTacToeGame, function (isToday, isPrevious) {
                var earnCredits = 0
                if (winnerIndex == 0) { earnCredits = 1 }
                if (winnerIndex == 1) { earnCredits = 4 }

                user.credits = user.credits + earnCredits
                user.lifetimeCredits = user.lifetimeCredits + earnCredits


                //increase ad today counter
                user.lastCompitedTicTacToeGame = (new Date()).getTime().toString()

                userBadgesController.checkIfUserNeedToWinBadgeForCredits(user, function (user) {

                  user.save(function (err, user) {
                    if (err) {
                      errorHandlingController.generalSendErrorWithMessage(req, res, 'serverError')
                    }
                    else {
                      var canPlayMoreGames = user.ticTacToeHearts > 0
                      if (earnCredits > 0) {
                        notificationController.sendNotificationToUserForNewTicTacToeGame(user, earnCredits)
                        rankingController.updateRankings(user, earnCredits, 'ticTacToe')
                      }

                      var canWatchAdToDoubleCredits = false
                      if (user.gamesWithoutAd >= ConfigParams.numberOfGamesForAd() && earnCredits > 0) {
                        canWatchAdToDoubleCredits = true
                      }
                      var timeLeftToNextHeart = (((ConfigParams.minutesForHeart() * 60000)) + parseInt(user.lastGivenHeart)) - ((new Date()).getTime())

                      var haveMaxHearts = user.mathGameHearts == ConfigParams.maxHearts() && user.memoryHearts == ConfigParams.maxHearts() && user.ticTacToeHearts == ConfigParams.maxHearts()
                      var canWatchVideoForHeart = true && haveMaxHearts == false
                      if (user.lastWatchedVideoForHeart) {
                        var canWatchVideoForHeart = (((ConfigParams.minutesForHeartWatchedAd() * 60000)) + parseInt(user.lastWatchedVideoForHeart)) - ((new Date()).getTime()) < 0 && haveMaxHearts == false
                      }

                      module.exports.sendMessageToSocketForComlitedGame(game, user._id.toString(), user.credits, earnCredits, canPlayMoreGames, canWatchAdToDoubleCredits, reasonForFinish, user.ticTacToeHearts, ConfigParams.maxHearts, null, user.gamesWithoutAd >= ConfigParams.numberOfGamesForAd() - 1, timeLeftToNextHeart, canWatchVideoForHeart)
                    }
                  })
                })
              })
            }
            else {
              var timeLeftToNextHeart = (((ConfigParams.minutesForHeart() * 60000)) + parseInt(user.lastGivenHeart)) - ((new Date()).getTime())

              var haveMaxHearts = user.mathGameHearts == ConfigParams.maxHearts() && user.memoryHearts == ConfigParams.maxHearts() && user.ticTacToeHearts == ConfigParams.maxHearts()
              var canWatchVideoForHeart = true && haveMaxHearts == false
              if (user.lastWatchedVideoForHeart) {
                var canWatchVideoForHeart = (((ConfigParams.minutesForHeartWatchedAd() * 60000)) + parseInt(user.lastWatchedVideoForHeart)) - ((new Date()).getTime()) < 0 && haveMaxHearts == false
              }

              module.exports.sendMessageToSocketForComlitedGame(game, user._id.toString(), user.credits, 0, false, false, reasonForFinish, user.ticTacToeHearts, ConfigParams.maxHearts, null, user.gamesWithoutAd >= ConfigParams.numberOfGamesForAd() - 1, timeLeftToNextHeart, canWatchVideoForHeart)
            }
          })
        }
      })
    }
  },

就像其他人在这里评论一样,通过查看代码很难找到明显的原因。

您应该查看的一件事可能是在您的应用程序中调用此函数的频率。 您对User.findOne调用。 由于连接池大小限制,可能会以某种方式限制,即排队或阻塞请求。

如果函数调用数多于可用资源/连接数,您将开始看到内存使用量逐渐增加。

不是说这是原因,但绝对是我会开始寻找的地方。

这里看起来可能有问题的是“游戏”对象和创建的用户对象。 因此,根据它们的去向(两者都被传递到sendMessageToSocketForComlitedGame ),它们可能是排队或以其他方式保留。 我在这里担心的是闭包泄漏,可能是一些数组或队列使它们保持活动状态,或者可能是计时器循环。 您是否尝试过将 'sendMessageToSocketForComlitedGame' 函数设为 noop 以查看内存问题是否消失?

另一个想法 - 当用户会话或游戏正在进行时,这可能是内存状态数据吗? 是否有其他一些长期存在的数据结构会在事件发生时被删除? 因为在这里它看起来不像是永久性泄漏,而是您将数据保留了一段时间。

由于您已经为此苦恼了几天,我相信您已经阅读了所有类型的关于 javascript 泄漏的文章,但我发现这是一篇不错的文章 我会查看这个函数的上方和下方,看看是否有任何东西保持对实例化闭包或对象的引用。

祝你好运! -达林

我不是在优化或指出导致内存泄漏的代码的任何漏洞,但这里有一些提示可以帮助您避免此类泄漏

全局变量:由于 JavaScript 中的全局变量是由根节点(window 或 global this)引用的,因此在应用程序的整个生命周期中它们永远不会被垃圾回收,并且只要应用程序运行就会占用内存。 这适用于全局变量及其所有子变量引用的任何对象。 从根引用大量对象可能会导致内存泄漏。

多个引用:当多个对象引用同一个对象时,如果其中一个引用悬空,可能会导致内存泄漏。

闭包: JavaScript 闭包有一个很酷的特性,那就是记住它周围的上下文。 当闭包在堆中持有对大对象的引用时,只要闭包在使用,它就会将对象保存在内存中。 这意味着您很容易遇到持有此类引用的闭包可能被不当使用导致内存泄漏的情况。

计时器和事件:当大量对象引用保留在回调中而没有正确处理时,使用 setTimeout、setInterval、Observers 和事件侦听器可能会导致内存泄漏。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM