Re: [問題] stack v.s. heap sizes

看板C_and_CPP (C/C++)作者 (眾生都是未來佛)時間15年前 (2010/08/30 11:47), 編輯推噓9(9071)
留言80則, 7人參與, 最新討論串2/3 (看更多)
我最近剛好又須要探討這個問題。 其實我一直對max stack size遠小於max heap size感到疑惑。 沒道理,automatic variables速度比dynamic variables快, compiler要限制max stack size遠小於max heap size。 雖然之前Ebergies大有給個理由,但我一直覺得怪怪的, 在最後文章會有我的論點。 所以我又上google去找原因。 我發現真的有人說[1]"能用stack就用stack。不能用stack,才用heap。" "In C++ use stack objects where you can, use heap objects when you have to," said Peter van Merkerk. "To summarize, use automatic storage whenever you can and dynamic storage only when you must," said Tom. 但這並沒有解決我所有疑惑:為什麼max stack size那麼小? max stack size那麼小,很容易就用光了。 後來我就去查Visual C++ max stack size是多少,但MSDN沒說多少,只說有上限[2]。 後來我只好自己去試。我發現我在Visual C++ 2010 Express的/stack設定 加了好多位數,都可以compile/link。 加到後來我驚覺一件事:Visucal C++早就可以叫很大的stack size。 後來我換成Visual C++ 2008 Express SP1,也是可以叫很大的stack size, 不是一般謠傳的16 MB[3]、32MB那麼小。 我的OS是Win 7 32-bit。 Visual C++ 2008的total automatic space上限是2GB,超過2GB會compile失敗。 我目前試出stack size可以到1.9G: http://pic.pimg.tw/zxvc/c4855512304ed246d7142a8fe037d428.png?v=1283134381 http://pic.pimg.tw/zxvc/48f26a14e566dbd9f3d401b96e104879.png?v=1283134381 不過上圖一的那個"Memory (Priva..."其實只算physical memory的使用量, 要叫出phy+virtual memory,可以View/Select Columns.../Memory - Commit Size ※ 引述《zxvc (修行)》之銘言: : 標題: [問題] stack v.s. heap sizes : 時間: Sat Sep 12 20:14:48 2009 : : 在Windows上stack最大可以設到32MB, : heap卻可以設定到2GB。 : : 請問為何這兩個限制大小差那麼多? : 靜態變數(在stack)不是效能比較好嗎!? : 為什麼需要配置大量空間不建議配置在stack? : : -- : ※ 發信站: 批踢踢實業坊(ptt.cc) : ◆ From: 140.115.221.79 : → suhorng:靜態變數!? 09/12 20:54 : → zxvc:應該說"靜態記憶體配置的變數"。 09/12 21:08 : 推 bobhsiao:stack是給local var用的, 通常幾十KB就夠了 09/12 22:12 : → bobhsiao:heap是給malloc/new用的, 使用量視AP而定 09/12 22:13 : 推 final01:heap可動態釋放!不過stack效能較好到是事實巴 09/12 23:06 : 推 Ebergies:這是邏輯上的思考, 通常 stack 是用來存取 function 內 09/12 23:15 : → Ebergies:的變數, 若 function 內的 local var 過大, 那麼這個 09/12 23:15 : → Ebergies:function 勢必不夠靈活, 光 recursive 沒幾次再大的 09/12 23:16 : → Ebergies:stack 也不夠用, 而這類超大空間的配置就使用上也大多 09/12 23:17 : → Ebergies:只需要配置很少的次數, 因此放在 heap 是合理且好的作法 09/12 23:17 不好意思,這樣有個假設, 就是所有漂亮的程式碼都不會是"遞迴且有很大的local variables"。 請問這能證出來嗎!?如果不能,這種coding style未必不好。 就算可能某些遞迴情形下,這些variables真的太大了, 難道"dynamic variables"就能解決這問題嗎? 如果max stack size可以跟max heap size一樣大(事實上stack真得可以很大), heap的1MB會等於stack的1GB嗎? 既然不會,沒道理"遞迴且有很大的variables,用stack不夠,用heap會夠!?" 又stack不一定是要用在遞迴, 比如我想在main function用到一個很大的local variable。 所以我認為heap的好處不是在因為function不能遞迴很多次, 是在於它能動態產生、釋放記憶體。 : → zxvc:謝謝樓上。 09/14 20:16 References: [1] http://www.velocityreviews.com/forums/t278261-stack-vs-heap.html [2] http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.71).aspx [3] http://bluecat.csie.net/2009/10/28/3924/ -- 信佛的人要知道:佛絕不會說謊。但請把握時光。 法滅盡經: http://www.cbeta.org/result/normal/T12/0396_001.htm 共勉之。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.115.73.148 ※ 編輯: zxvc 來自: 140.115.73.148 (08/30 12:39)

08/30 12:59, , 1F
stack 是靜態預先配置出來的記憶體空間,配都配了,
08/30 12:59, 1F

08/30 12:59, , 2F
能用當然就要盡量用。如果你把 stack 設定成 1G,
08/30 12:59, 2F

08/30 13:00, , 3F
你的程式一啟動,記憶體就吃掉 1G 了。
08/30 13:00, 3F

08/30 13:02, , 4F
雖然一般計概是告訴你 call 進 function 會 push 一個
08/30 13:02, 4F

08/30 13:02, , 5F
stack frame,但這不代表空間是在 push 的時候才配置。
08/30 13:02, 5F

08/30 13:02, , 6F
那個 push 的動作只是把 stack pointer 跳一下,當然相對
08/30 13:02, 6F

08/30 13:02, , 7F
來說 pop 也只是向下跳。在沒有使用 alloca() 的前提下,
08/30 13:02, 7F

08/30 13:03, , 8F
每個 function 的 stack frame size 是被 compiler 靜態
08/30 13:03, 8F

08/30 13:03, , 9F
預先計算好的。所以 stack 上的各種操作只是單純的
08/30 13:03, 9F

08/30 13:04, , 10F
算術運算。heap 上就真的有配置、釋放的動作,速度有差。
08/30 13:04, 10F

08/30 13:04, , 11F
如果我是你的客戶,開個小程式就要吃 1.9G 的記憶體,
08/30 13:04, 11F

08/30 13:04, , 12F
我一定打電話去你公司客服狗幹。
08/30 13:04, 12F

08/30 13:08, , 13F
heap 的狀況不一樣,因為它的位置在後面。lib 的 malloc()
08/30 13:08, 13F

08/30 13:09, , 14F
實作發現 heap 容量不足時,才會用系統呼叫跟 OS 要記憶體
08/30 13:09, 14F

08/30 13:10, , 15F
。所以幾乎是用多少配多少 (當然還是會預留一些空間),
08/30 13:10, 15F

08/30 13:10, , 16F
這跟 stack 一開始就配下去的狀況完全不同。
08/30 13:10, 16F

08/30 13:14, , 17F
而且在 user space 只有 2G 的虛擬記憶體模型下,stack
08/30 13:14, 17F

08/30 13:14, , 18F
就佔了那麼多,相對的你的 code / data / heap 可用空間就
08/30 13:14, 18F

08/30 13:15, , 19F
受到限制。malloc() 也許常常傳回 NULL。
08/30 13:15, 19F

08/30 13:24, , 20F
heap可以有很多個,stack通常只有一個
08/30 13:24, 20F

08/30 13:27, , 21F
t大,三樓的確是stack的一個缺點,這應該才是stack有時不能太
08/30 13:27, 21F

08/30 13:27, , 22F
大的原因吧。
08/30 13:27, 22F

08/30 13:31, , 23F
上面有個地方沒講清楚,heap 的位置被配在後面並不代表
08/30 13:31, 23F

08/30 13:32, , 24F
是在 stack 後面。stack 一般是被 OS 配置在最高位址區。
08/30 13:32, 24F

08/30 13:33, , 25F
然後開 thread 的時候,會出現第二個 stack。
08/30 13:33, 25F

08/30 13:34, , 26F
如果為了追求速度,且寫程式只是為了特定目的(如作研究),這
08/30 13:34, 26F

08/30 13:35, , 27F
時是可以多利用stack的好處的。
08/30 13:35, 27F

08/30 13:36, , 28F
l大,那會造成什麼問題?
08/30 13:36, 28F

08/30 13:38, , 29F
不一定啦,你可以在 heap 一次 allocate 超大的空間做成
08/30 13:38, 29F

08/30 13:38, , 30F
memory pool,這樣也能大幅節省配置/釋放的時間開銷。
08/30 13:38, 30F

08/30 13:57, , 31F
同意t大,"超大空間、很少次配置,用heap也不會太差",E大也
08/30 13:57, 31F

08/30 13:58, , 32F
有提到這一點。至於有沒有"超大空間,很多次配置"的這種應用
08/30 13:58, 32F

08/30 13:59, , 33F
就不清楚有沒有了。
08/30 13:59, 33F

08/30 15:10, , 34F
stack配置在最高位址區是linux/unix才這樣吧?
08/30 15:10, 34F

08/30 15:11, , 35F
windows放得很前面
08/30 15:11, 35F

08/30 15:14, , 36F
windows 就不清楚了,C 最初是拿來寫 UNIX,所以大部分書
08/30 15:14, 36F

08/30 15:14, , 37F
的例子都是舉 UNIX。加上 stack 跟 heap 有必要放在兩個
08/30 15:14, 37F

08/30 15:15, , 38F
相對的端點上,然後向中間生長。
08/30 15:15, 38F

08/30 15:16, , 39F
heap 可以往中間 grow,stack 會隨著 thread 數量往中間
08/30 15:16, 39F

08/30 15:16, , 40F
allocate。任一種做法都會壓縮到對方未來可配置的空間。
08/30 15:16, 40F

08/30 15:17, , 41F
反過來設計應該也沒什麼不好,重點是 heap 要有地方 grow
08/30 15:17, 41F

08/30 15:18, , 42F
windows的heap切成好幾個區塊,然後用一個heap table記
08/30 15:18, 42F

08/30 15:18, , 43F
錄其範圍,我不太喜歡這種作法
08/30 15:18, 43F

08/30 15:19, , 44F
之前看 exceptional C++ 有說 C++ 其實是用 free storage
08/30 15:19, 44F

08/30 15:20, , 45F
配置動態記憶體空間,不保證 free storage == C 的 heap。
08/30 15:20, 45F

08/30 15:20, , 46F
其它地方就比較少聽到這類說法,但從這點聽起來 heap 被
08/30 15:20, 46F

08/30 15:21, , 47F
分成多個區域似乎也還算合理。只是可能要考慮 fragment
08/30 15:21, 47F

08/30 15:21, , 48F
的問題。
08/30 15:21, 48F

08/30 15:23, , 49F
不過如果要一次 allocate 1.5G 的 array,
08/30 15:23, 49F

08/30 15:23, , 50F
我蠻好奇這樣切開來要怎麼 allocate。
08/30 15:23, 50F

08/30 21:40, , 51F
t大,我後來發現Windows並不是stack reserve size設多少
08/30 21:40, 51F

08/30 21:40, , 52F
physical mem就被吃多少。
08/30 21:40, 52F

08/30 21:42, , 53F
比如,設個很大的stack reserve size,然後完全沒宣告任何
08/30 21:42, 53F

08/30 21:43, , 54F
automatic variables,用Windows Task Manager看會發現沒有
08/30 21:43, 54F

08/30 21:43, , 55F
真的吃到那麼多記憶體。
08/30 21:43, 55F

08/30 21:45, , 56F
另外我這篇對virtual memory的部分有些誤解,大家先忽略那幾
08/30 21:45, 56F

08/30 21:45, , 57F
句話。
08/30 21:45, 57F

08/30 22:07, , 58F
process 是在 virtual address space 上工作的,所以當然
08/30 22:07, 58F

08/30 22:08, , 59F
不會在你沒使用到的時候就去佔到實體記憶體。
08/30 22:08, 59F

08/30 22:09, , 60F
但是這完全依賴 OS 的實作,如果很不幸的你偏偏就只用到那
08/30 22:09, 60F

08/30 22:09, , 61F
麼大的空間一次而已,實體空間就這樣被你佔下來。後續其它
08/30 22:09, 61F

08/30 22:10, , 62F
process 也要佔用實體記憶體時,swap out 就會拖慢速度。
08/30 22:10, 62F

08/30 22:11, , 63F
如果是在沒有虛擬記憶體的 real-time system,這樣搞就會
08/30 22:11, 63F

08/30 22:11, , 64F
直接往實體記憶體吃下去了。
08/30 22:11, 64F

08/30 22:12, , 65F
但是最大的問題還是在於,32-bit 系統上定址空間有限。
08/30 22:12, 65F

08/30 22:12, , 66F
你如果是 user/kernel = 2G/2G 的 model,stack 一吃就把
08/30 22:12, 66F

08/30 22:12, , 67F
user space 的 90% 吃光光,你 heap 的可用空間就會受限。
08/30 22:12, 67F

08/30 22:13, , 68F
另一個問題就在於你無法開 thread,因為開 thread 會由 OS
08/30 22:13, 68F

08/30 22:13, , 69F
配置第二個 stack 出來給新的 thread 用。
08/30 22:13, 69F

08/30 22:17, , 70F
如果你想讓 stack 變成跟 heap 一樣會動態增長,
08/30 22:17, 70F

08/30 22:18, , 71F
不是一開始就 allocate 好,那就得在每個 function call
08/30 22:18, 71F

08/30 22:18, , 72F
跟 return 的位置做一些 stack size 的檢查,然後決定
08/30 22:18, 72F

08/30 22:19, , 73F
是不是要跟 OS 要更多 stack 的空間,程式會又慢又肥。
08/30 22:19, 73F

08/30 23:14, , 74F
推t大的說明:)
08/30 23:14, 74F

08/30 23:29, , 75F
推 t 大熱心, 好久沒看到 v 大 XD
08/30 23:29, 75F

08/30 23:30, , 76F
我覺得這篇推文很有價值 等下看怎樣收進精華區
08/30 23:30, 76F

08/30 23:34, , 77F
放這 z-10-13
08/30 23:34, 77F

08/31 01:01, , 78F
小弟我現在公司不能上PTT, 只能回家抽空看Q_Q~
08/31 01:01, 78F

08/31 08:48, , 79F
是被發現然後檔掉了嗎.XD
08/31 08:48, 79F

08/31 21:51, , 80F
是跳槽了新東家有擋啦XD
08/31 21:51, 80F
文章代碼(AID): #1CUocyDl (C_and_CPP)
討論串 (同標題文章)
本文引述了以下文章的的內容:
以下文章回應了本文
完整討論串 (本文為第 2 之 3 篇):
文章代碼(AID): #1CUocyDl (C_and_CPP)