2016-10-04 73 views
1

僅僅爲了獲得樂趣並嘗試nodejs,我寫了一個非常非常簡單的程序,用於測試Collat​​z猜想中的荒謬數量。理論上這應該沒問題。我遇到的問題是這個超級簡單的代碼有內存泄漏,我無法確定原因。簡單nodejs中的內存泄漏app

var step; 
var numberOfSteps; 
for (var i = 0; i < 100000000000000; i++) { 
    step = i; 
    numberOfSteps = 0; 
    while (step !== 1) { 
     if (step%2 === 0) 
      step /= 2; 
     else 
      step = 3 * step + 1; 
     numberOfSteps++; 
    } 
    console.log("" + i + ": " + numberOfSteps + " steps."); 
} 

我已經嘗試了循環內外的變量。我試過在循環結束時將它們置零。沒有什麼改變內存泄漏。

+0

泄漏在哪裏?我在我的電腦上試了一下,我的內存只增加了小於0.01G – Turtle

+2

沒有泄漏......但while循環對於步驟=== 0是**無限** ......但是,如果你解決了這個問題,節點似乎保持慢慢吞噬內存,也不是 –

+2

這是'console.log'這是造成它 - 它幾乎如果GC是不能夠運行由的console.log調用清理垃圾落在了後面 –

回答

1

調查了一下我的核心轉儲:

<--- Last few GCs ---> 

    131690 ms: Scavenge 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1.3/0 ms (+ 2.8 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep]. 
    132935 ms: Mark-sweep 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1245.0/0 ms (+ 3.7 ms in 2 steps since start of marking, biggest step 2.8 ms) [last resort gc]. 
    134169 ms: Mark-sweep 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1234.5/0 ms [last resort gc]. 


<--- JS stacktrace ---> 

==== JS stack trace ========================================= 

Security context: 0x33083d8e3ac1 <JS Object> 
    1: /* anonymous */ [/user/projects/test.js:~1] [pc=0x557d307b271] (this=0x2a4a669d8341 <an Object with map 0xf8593408359>,exports=0x33083d804189 <undefined>,require=0x33083d804189 <undefined>,module=0x33083d804189 <undefined>,__filename=0x33083d804189 <undefined>,__dirname=0x33083d804189 <undefined>) 
    3: _compile [module.js:413] [pc=0x557d304d03c] (this=0x2a4a669d8431... 

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory 
Aborted (core dumped) 

看來,這是關於執行console.log一個已知的問題,根據GitHub上這個問題https://github.com/nodejs/node/issues/3171

This is a known "issue" since writing to stdout in the case of a tty/console is async. So logging a lot of data very fast could very well cause a lot of writes to be buffered in memory if the tty/console cannot keep up.

1

這裏的,這似乎代碼峯值約爲50MB對我來說

這個函數執行10000個函數 - 用setImmediate來處理下一個批次

function collatz(n) { 
    var step,numberOfSteps, i; 
    for(i = 0; i < 10000; i++, n++) { 
     step = n; 
     numberOfSteps = 0; 
     while (step !== 1) { 
      if (step%2 === 0) 
       step /= 2; 
      else 
       step = 3 * step + 1; 
      numberOfSteps++; 
     } 
     console.log("" + n + ": " + numberOfSteps + " steps."); 
    } 
    if (n < 100000000000000) { 
     setImmediate(collatz, n); 
    } 
} 
collatz(1); 

注意,在這種情況下,你可以在0具有for循環的開始,因爲n將在1日開始:對

我沒有for循環

我試過的較高值我們已經對原始代碼做了一些基準測試 - 一次執行100次(在for循環中)的性能與10000相同,並且與原始代碼在性能上沒有區別。即使一次只有10個,我也不會說這個方法比較慢。一次只有1個,它比原始代碼慢5-8%慢

Note, I originally thought the issue was garbage collection (or lack thereof) due to the tight loop giving node no time to do any house keeping, but while I was posting the answer, @Svabel posted what seems to be a known issue with hitting console.log hard.

I can only assume that using setImmediate allows some sort of house keeping regarding the tty buffers that is otherwise not possible.

+0

你能解釋爲什麼使它異步解決內存問題嗎? – Bergi

+0

不是真的,我認爲這可能是一個GC的問題,但閱讀@ Svabel的答案,也可能是是與有關的console.log「已知的問題」 - 我不希望包括在我的答案爲似乎不合適 –

+1

有趣,我在「問題」看,使用'setImmediate'是提出的解決方案 - 每個迭代一個setImmediate - 10000每次迭代是矯枉過正,甚至100給出相似的(實際上超過了一些測試,我可以」說它更糟糕)「性能」的原始代碼 - 而每迭代1確實有約8%的速度影響速度 –