基于c或c++的堆棧,操作系統(tǒng)為linux
堆內(nèi)存和棧內(nèi)存都是位于主存上的內(nèi)存匠抗,為什么堆內(nèi)存需要開發(fā)者申請故源、釋放內(nèi)存,而棧內(nèi)存則不需要汞贸?
相同點
- 堆和棧都是位于主存上的內(nèi)存
- 都由操作系統(tǒng)管理
不同點
- 棧是LIFO(后進先出)的數(shù)據(jù)結(jié)構(gòu)
- 每個申請的堆內(nèi)存是一塊連續(xù)內(nèi)存绳军,堆內(nèi)存之間是不連續(xù)的
- 棧內(nèi)存大小可以針對單個進程設(shè)置上限。通過設(shè)置ulimit中的Max stack size來限制單個進程的最大棧內(nèi)存(默認值為8M)矢腻,也可以在程序main方法最開始的地方調(diào)用系統(tǒng)方法setrlimit來修改最大棧內(nèi)存删铃,子進程的最大棧內(nèi)存繼承自父進程。查看指定進程的最大棧內(nèi)存
cat /proc/進程id/limits
- 堆內(nèi)存沒有上限踏堡,只有OOM
- 棧內(nèi)存是由高地址向低地址向下增長猎唁,局部變量存儲在棧內(nèi)存上。當需要新的局部變量時顷蟆,棧頂向下移動诫隅,將變量內(nèi)存分配在棧頂;當函數(shù)執(zhí)行完成返回帐偎,函數(shù)中的局部變量不再有用逐纬,棧頂向上移動銷毀變量。下一個函數(shù)的局部變量的內(nèi)存分配與消耗繼續(xù)如此削樊。棧頂移動時豁生,不會清空內(nèi)存中的數(shù)據(jù),所以局部變量需要初始化漫贞。當初始分配的椀橄洌空間大小不足時,系統(tǒng)會自動擴容迅脐。
- 堆內(nèi)存是由低地址向高地址增長芍殖。與棧內(nèi)存的數(shù)據(jù)結(jié)構(gòu)不同,堆內(nèi)存是零散的內(nèi)存塊谴蔑,它的分配與釋放需要開發(fā)者手動管理豌骏。當我們申請了一塊堆內(nèi)存且不釋放龟梦,它在我們進程的整個生命周期內(nèi)都是可用的,這與棧內(nèi)存中的局部變量隨著函數(shù)的返回而銷毀不同窃躲。這樣也容易出現(xiàn)訪問已釋放的堆內(nèi)存和堆內(nèi)存忘記釋放计贰,即懸空指針(指針指向的內(nèi)存已釋放,指針本身沒有置空)和內(nèi)存泄漏蒂窒。野指針(指針變量未初始化躁倒,指針中的地址是隨機的;指針指向了不可訪問的地址:如已釋放的棧內(nèi)存地址或者越界或者指針運算跑飛了)
- 棧內(nèi)存是基于線程的刘绣,每個線程擁有自己的棧內(nèi)存
- 堆內(nèi)存是進程內(nèi)共享的,每個線程都可以訪問
- 棧內(nèi)存是一次性分配足夠大的空間(不足時會擴容)挣输,存儲變量時不需要進行內(nèi)存分配
- 堆內(nèi)存每次分配都需要向操作系統(tǒng)申請(或者你申請足夠大的內(nèi)存纬凤,自己手動做內(nèi)存管理)
- 棧內(nèi)存是連續(xù)的,滿足局部性原理撩嚼,cpu緩存命中高
- 堆內(nèi)存是分散的停士,cpu緩存命中沒有棧內(nèi)存高
go語言中的堆和棧
由于go的運行時自己做了內(nèi)存管理,且有g(shù)oroutine的存在(需要為每個goroutine分配棧)完丽,go語言中的堆內(nèi)存和棧內(nèi)存都是在主存中堆空間申請的恋技。
go自行管理的棧初始大小為2k,需要時會進行擴縮容