對於每個Gopher來說,理解記憶體的分配也是很重要的一環,雖然不用像C/C++一樣完全做手動分配記憶體的管理,Go本身有GC(垃圾回收機制),如同Java/.Net/etc類的自動化管理方式

雖然效能可能不及C/C++來的有效率,因為工程師要很清楚知道記憶體什麼時候不用要釋放掉,相較於golang來說也許捨棄點效能把重要的的事情交給自動化來管理也許是golang的一大優勢

Stack(棧)

  • 靜態配置記憶體
  • Last In First Out(LIFO)後進先出的方式
  • 用來儲存物件的stack與run-time的call stack運作原理一樣
  • run-time的stack frame包含:Parameters(函數的參數),Return Address(回傳地址),Local Variable(區域變數)
  • 用英文比較直觀好記

[name=reference] A call stack is a LIFO stack data structure that stores arguments, local variables, and other data tracked as a thread executes functions

可以理解成將可預期的變數或是func…等處理方式

例如:編輯器(word,goland)中的undo功能,瀏覽器回到上一頁

注意: 由於stack不會隨程式增長而增加,如果func呼叫的深度過大,stack的容量會不足,此時會導致程式崩潰,此現象稱為stack-overflow,一般出現在無限遞迴時發生。

Heap(堆)

  • 動態配置記憶體,需自行回收(golang使用GC動態回收)
  • 有別於stack,heap在程式中運行大小不固定,因此也可以自由的請求或釋放記憶體
  • 若GC處理不好,就會有記憶體狂吃情況
  • 將不可預期的變數或資料分配管理,可以被GC回收的處理方式
  • Goroutine以這方式進行生命週期
  • 如使用指針或是變數大小在編譯時未知情況,編譯器會將變量放到heap上去,來確保要取直的時候,值總是正確的

總結:

Go在編譯期間會進行逃逸分析,判斷並標記變數是否需要分配到heap上面,例如創見Map,Slice的時候

逃逸分析可以透過指令集-gcflags 進行分析,如下(下個章節在討論如何用go實做逃逸分析)

go build -gcflags '-m -l' main.go

對於go來說底層分配到stack或heap上,由於透過逃逸分析來確認,實際上對你來說是不用太過於擔心的

至於是否每個地方都使用指標來減少記憶體的分配不一定是最好的,也是要看設計情況而定,畢竟程式運行的穩定度永遠都比效能來的更重要

參考:

https://studygolang.com/articles/30505 https://juejin.cn/post/7135670650353483783 https://eddycjy.gitbook.io/golang/di-1-ke-za-tan/stack-heap https://roykwokcode.medium.com/%E6%99%AE%E9%80%9A%E9%A1%9E%E5%9E%8B%E5%92%8C%E5%B0%8D%E8%B1%A1%E7%9A%84%E5%8D%80%E5%88%A5-%E6%A3%A7%E5%85%A7%E5%AD%98-stack-%E5%A0%86%E5%85%A7%E5%AD%98-heap-44295724848c