簡體   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