Z-jane
從題目的意思來看,題主問的是編程裡變量佔用的內存為什麼要分堆和棧,而不是堆和棧兩種數據結構的不用以及函數調用棧等問題。從這個角度來說的話,變量的內存要分為堆和棧的根本原因其實是因為棧空間不夠大!
眾所周知,棧空間是屬於進程空間的,而且棧又要與代碼段、數據段等共享進程空間的,而操作系統分給每個進程的空間是有限的,所以棧空間也是有限的。有限的棧空間,就不允許程序員在代碼裡聲明佔用太大內存空間的變量,而當程序必須使用很大的內存空間時,就需要用到堆內存。
堆內存,理論上和電腦上可用的物理內存一樣大,但堆空間的使用不像棧那樣系統自動幫你分配和釋放,而是需要程序員手工按需分配、使用完成後手工釋放——好處就是夠大!
所以說,編程裡的變量如果只使用棧空間,對於需要太大內存的變量是不可行的。但是,只使用堆空間理論上是可行的,但是由於堆空間使用起來的不方便,而且稍有不慎輕則造成內存洩漏、重則導致程序崩潰。因此,編程過程中,還是需要根據實際情況,來選擇是使用“棧”還是“堆”。
小莊讀書
其實我們都知道,計算機內存本來就是一塊內存,沒有堆棧之分。
在學編程的時候,我們應該都聽過一句話 “如果程序結束之後仍然想要訪問那一段數據就要用堆”,我想這個其實就是你疑問的關鍵了,堆和棧都有其自己的獨特性,可能你瞭解這兩個東西,但是我還是解釋下,以免別的小夥伴在看答案的時候,不知道。
棧:就像我第一句話說的,本沒有什麼堆棧之分,但是編程語言的出現,就有了一個概念“函數”,這個函數之間是可以相互調用的(就像我們傳遞東西,比如:胡小然 將東西傳遞 胡小然2 將東西傳遞 胡小然3,之後需要從後面向前面反饋傳遞結果,這個傳遞的過程我們就可以理解為調用),那就出現了前後之分,這就是調用隊列了,那這個隊列有個什麼特點呢,那就是先被調用進入隊列的要最後出去,就是我們常說的先進後出(FILO),那麼這時棧就出現了,而且它還有一個特點那就是線程獨有(所以可以存放我們的臨時變量),生命週期是隨線程的。當然我所說的是內存棧的意思,其實“棧”就是個數據結構,是一種限定僅在表尾進行插入和刪除操作的線性表,這個特性不正好是符合我剛才說的FILO嘛。所以你可以這麼理解c++或者java(jvm)中的內存棧的概念,就是編程語言的作者為了管理內存使用了“棧”這種數據結構(說的再細點就是現代CPU體系結構決定了棧是管理函數調用和局部變量的最佳數據結構。因為CPU已經提供了現成的指令)。
堆:可算是一種特殊的數據結構,好像我們經常使用的二叉樹。內存堆這個解釋起來就更簡單了,就是一塊能自由分配的內存。它允許程序在運行時動態地申請某個大小的內存空間,比如:程序員向操作系統申請一塊內存,當系統收到程序的申請時,會遍歷一個記錄空閒內存地址的鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。其特點就是分配的速度較慢,地址不連續,容易碎片化並且是由程序員申請,同時也必須由程序員負責銷燬,否則導致內存洩露。像在java這種高級語言中,我們不比擔心內存回收的問題,那是因為jvm已經在幫我們處理了。
上面說了這麼多,就是想說明一下內存棧和內存堆出現的意義和作用,所以答案就出來了,那就是不能“只用堆或者全部只用棧”那樣我們程序的調用和數據的存儲都會出現問題。
好了,上面只是我自己一些理解,不對的地方還請大家指出,也希望對題主的疑問有幫助。
唯一胡小然
一個非常好的問題。堆棧是典型的兩類內存分配方式,都是為了優化內存的使用,各有特點,用在不同的場景中。
一,棧
棧具有先進後出,後進先出特性,連續存儲,操作簡單,使用方便,無需管理,大部分芯片都對棧提供芯片級別的硬件支持,只需要移動指針就可以快速實現內存的分配和回收。比如局部變量使用棧內存,減少不必要的內存分配管理。
棧創建和刪除的時間複雜度是O(1),速度快。但是不利於管理大內存,棧中的數據大小和生存週期都是確定的,缺乏靈活性。
二,堆
堆內存的管理機制相對複雜,有一套相應的分配策略,防止大量小碎片出現,同時加快查找。堆用於動態創建分配內存,創建和刪除節點的時間複雜度是O(logn)。
堆的回收機制也複雜很多,根據內存大小不同,數據生命週期不同,採用相應的回收機制,涉及操作系統的堆管理。
因為堆內存的管理和申請相對複雜,更消耗系統資源,通常生命週期更長使用範圍更廣的全局變量使用堆內存。