2016-02-29 28 views
3

以下代碼聲明瞭兩個數組,然後迭代stdin(只是盲目地迭代文件 - 不與數組交互)。當聲明非常大的數組並迭代stdin時,golang會增加連續內存

這是造成內存不斷增加的原因。

但是,如果我只聲明兩個數組並睡眠 - 內存沒有增加。

同樣,如果我只是遍歷stdin - 內存沒有增加。

但是在一起(除了分配給陣列的內存之外)還在不斷增加。

我通過使用頂級工具查看RES內存來衡量這一點。

我已將func doSomething()中的前幾行註釋掉,表明在註釋時沒有內存增加。取消註釋和運行會導致增加。

注意:您將需要至少16GB的RAM重新創建此一臺機器上,因爲我已經在只觀察到的那樣:這是在去1.4.2,1.5.3和1.6

注意運行陣列大小爲10億。

package main 

import (
    "bufio" 
    "fmt" 
    "io" 
    "os" 
) 

type MyStruct struct { 
    arr1 []int 
    arr2 []int 
} 

func (ms *MyStruct) Init(size int, arr1 []int, arr2 []int) error { 
    fmt.Printf("initializing mystruct arr1...\n") 
    ms.arr1 = arr1 
    if ms.arr1 == nil { 
     ms.arr1 = make([]int, size, size) 
    } 
    fmt.Printf("initializing mystruct arr2...\n") 
    ms.arr2 = arr2 
    if ms.arr2 == nil { 
     ms.arr2 = make([]int, size, size) 
    } 
    fmt.Printf("done initializing ...\n") 
    for i := 0; i < size; i++ { 
     ms.arr1[i] = 0 
     ms.arr2[i] = 0 
    } 
    return nil 
} 

func doSomething() error { 
    fmt.Printf("starting...\n") 
    fmt.Printf("allocating\n") 
    /* NOTE WHEN UNCOMMENTED CAUSES MEMORY INCREASE 
    ms := &MyStruct{} 
    size := 1000000000 
    ms.Init(size, nil, nil) 
    */ 

    fmt.Printf("finished allocating..%d %d\n", len(ms.arr1), len(ms.arr2)) 

    fmt.Printf("reading from stdin...\n") 
    reader := bufio.NewReader(os.Stdin) 

    var line string 
    var readErr error 
    var lineNo int = 0 
    for { 
     if lineNo%1000000 == 0 { 
      fmt.Printf("read %d lines...\n", lineNo) 
     } 
     lineNo++ 

     line, readErr = reader.ReadString('\n') 
     if readErr != nil { 
      fmt.Printf("break at %s\n", line) 
      break 
     } 
    } 

    if readErr == io.EOF { 
     readErr = nil 
    } 

    if readErr != nil { 
     return readErr 
    } 

    return nil 
} 

func main() { 
    if err := doSomething(); err != nil { 
     panic(err) 
    } 
    fmt.Printf("done...\n") 
} 
  1. 這是我的代碼的問題嗎?或者是去系統做一些意想不到的事情?
  2. 如果是後者,我該如何去調試呢?

爲了更容易在這裏複製是很好的情況下(上述代碼的註釋部分)和不好的情況下引擎收錄文件(取消註釋部分)

wget http://pastebin.com/raw/QfG22xXk -O badcase.go 
yes "1234567890" | go run badcase.go 

wget http://pastebin.com/raw/G9xS2fKy -O goodcase.go 
yes "1234567890" | go run goodcase.go 
+0

你如何衡量「增加內存」以及你在討論哪種類型的內存。很可能一切都很好;瀏覽你的代碼看起來沒問題。當然,如果你分配20億的內存,你的代碼會消耗更多的內存,因爲這至少是8 GB。 – Volker

+0

@Volker我啓動一個htop並監視RES內存。是的,在64位計算機上啓動時大約需要16 GB,但是當我從stdin開始閱讀時,它就會啓動並永不停止。 (更新了問題以反映評論)。 – algrebe

+0

你確定它確實不停止嗎?要看看記憶,請遵循http://dave.cheney.net/2015/11/29/a-whirlwind-tour-of-gos-runtime-environment-variables,特別是GODEBUG和gctrace,它比(h )頂部 – Volker

回答

2

謝謝Volker您的上述評論。我想捕捉調試這個過程作爲答案。

RES top/htop只是在進程級別上告訴你內存正在發生什麼。 GODEBUG =「gctrace = 1」可讓您更深入地瞭解內存如何處理。

與gctrace一套簡單的運行提供了以下

[email protected] ~ # yes "123456789" | GODEBUG="gctrace=1" go run badcase.go 
starting... 
allocating 
initializing mystruct arr1... 
initializing mystruct arr2... 
gc 1 @0.050s 0%: 0.19+0.23+0.068 ms clock, 0.58+0.016/0.16/0.25+0.20 ms cpu, 7629->7629->7629 MB, 7630 MB goal, 8 P 
done initializing ... 
gc 2 @0.100s 0%: 0.070+2515+0.23 ms clock, 0.49+0.025/0.096/0.24+1.6finished allocating..1000000000 1000000000 
ms cpu, 15258->15258reading from stdin... 
->15258 MB, 15259read 0 lines... 
MB goal, 8 P 
gc 3 @2.620s 0%: 0.009+0.32+0.23 ms clock, 0.072+0/0.20/0.11+1.8 ms cpu, 15259->15259->15258 MB, 30517 MB goal, 8 P 

read 1000000 lines... 
read 2000000 lines... 
read 3000000 lines... 
read 4000000 lines... 
.... 
read 51000000 lines... 
read 52000000 lines... 
read 53000000 lines... 
read 54000000 lines... 

這是什麼意思?

正如你所看到的,gc暫時還沒有被調用。這意味着從reader.ReadString生成的所有垃圾都沒有被收集和釋放。

垃圾回收器爲什麼不收集垃圾?

The go gc

相反,我們提供了一個單一的旋鈕,稱爲GOGC。此值控制堆的總大小相對於可訪問對象的大小的 。 默認值100意味着總堆大小現在比最後的 集合之後的可到達對象的大小大(即,兩倍)大100%大 。

由於GOGC未設置 - 默認值爲100%。所以,只有當它達到〜32GB時纔會收集垃圾。 (由於最初這兩個陣列給你16GB的堆空間 - 只有當堆倍增時纔會觸發gc)。

我該如何改變這種情況? 嘗試設置GOGC = 25。

隨着GOGC 25

[email protected] ~ # yes "123456789" | GODEBUG="gctrace=1" GOGC=25 go run badcase.go 
starting... 
allocating 
initializing mystruct arr1... 
initializing mystruct arr2... 
gc 1 @0.051s 0%: 0.14+0.30+0.11 ms clock, 0.42+0.016/0.31/0.094+0.35 ms cpu, 7629->7629->7629 MB, 7630 MB goal, 8 P 
done initializing ... 
finished allocating..1000000000 1000000000 
gc 2 @0.102s reading from stdin... 
12%: 0.058+2480+0.26 ms clock, 0.40+0.022/2480/0.10+1.8 ms cpu, 15258->15258->15258 MB, 15259 MB goal, 8 P 
read 0 lines... 
gc 3 @2.584s 12%: 0.009+0.20+0.22 ms clock, 0.075+0/0.24/0.046+1.8 ms cpu, 15259->15259->15258 MB, 19073 MB goal, 8 P 
read 1000000 lines... 
read 2000000 lines... 
read 3000000 lines... 
read 4000000 lines... 
.... 
read 19000000 lines... 
read 20000000 lines... 
gc 4 @6.539s 4%: 0.019+2.3+0.23 ms clock, 0.15+0/2.1/12+1.8 ms cpu, 17166->17166->15258 MB, 19073 MB goal, 8 P 

正如你所看到的,另一個GC被觸發。

但是top/htop顯示它穩定在〜20 GB而不是計算的16GB。

垃圾收集器不會「有」將其返回給操作系統。它有時會保持它在未來有效使用。它不必繼續從操作系統中獲取回饋 - 在再次詢問操作系統之前,額外的4 GB可用空間可用。

+0

@Volker感謝您的所有幫助。我試圖在這個答案中儘可能地捕獲它。如果你有什麼更有用的添加隨意編輯! – algrebe

相關問題