堆和棧, 他們是什么?
堆
- 堆包含一個
鏈表
來維護已用和空閑的內存塊. 在堆上新分配內存是從空閑的內存塊中找到一些滿足要求的合適塊. 這個操作會更新堆中的塊鏈表. 這些元信息也存儲在堆上, 經常在每個塊的頭部一個很小區(qū)域.
- 堆包含一個
- 堆增加新塊, 通常從低地址向高地址擴展. 因此你可以認為堆隨著內存分配而不斷的增加大小. 如果申請的內存很小的話, 通常從底層操作系統(tǒng)中得到比申請的要多的內存
- 申請和釋放許多小的塊, 可能會產生如下狀態(tài): 在已用塊之間存在很多小的空閑塊. 進而申請大塊內存時失敗, 雖然空閑塊的總和足夠, 但是空閑的小塊是零散的, 不能滿足申請的大小; 這叫做"堆碎片";
- 4.當旁邊有空閑塊的已用塊被釋放時, 新的空閑塊可能會與相鄰的空閑塊合并為一個大的空閑塊, 這樣可以有效的減少
堆碎片
的產生.
棧
- 棧經常與 stack pointer寄存器一起工作, 最初 sp 指向棧頂.
- CPU 用 push 指令來將數(shù)據壓棧[sp值減小,向低地址擴展], 用pop 指令彈棧[sp值增大].
- 當函數(shù)被調用時崇决,CPU使用特定的指令把當前的 IP (譯者注:“instruction pointer”,是一個寄存器,用來記錄 CPU 指令的位置)壓棧蕴坪。即執(zhí)行代碼的地址。CPU 接下來將調用函數(shù)地址賦給 IP ,進行調用肋乍。當函數(shù)返回時,舊的 IP 被彈棧敷存,CPU 繼續(xù)去函數(shù)調用之前的代碼墓造。
- 4.當進入函數(shù)時堪伍,sp 向下擴展,擴展到確保為函數(shù)的局部變量留足夠大小的空間觅闽。如果函數(shù)中有一個 32-bit 的局部變量會在棧中留夠四字節(jié)的空間帝雇。當函數(shù)返回時,sp 通過返回原來的位置來釋放空間蛉拙。
- 5.如果函數(shù)有參數(shù)的話尸闸,在函數(shù)調用之前,會將參數(shù)壓棧孕锄。函數(shù)中的代碼通過 sp 的當前位置來定位參數(shù)并訪問它們吮廉。
- 6.函數(shù)嵌套調用和使用魔法一樣,每一次新調用的函數(shù)都會分配函數(shù)參數(shù)畸肆,返回值地址宦芦、局部變量空間、嵌套調用的活動記錄都要被壓入棧中轴脐。函數(shù)返回時调卑,按照正確方式的撤銷。
- 7.棧要受到內存塊的限制大咱,不斷的函數(shù)嵌套/為局部變量分配太多的空間恬涧,可能會導致棧溢出。當棧中的內存區(qū)域都已經被使用完之后繼續(xù)向下寫(低地址)徽级,會觸發(fā)一個 CPU 異常气破。這個異常接下會通過語言的運行時轉成各種類型的棧溢出異常聊浅。(譯者注:“不同語言的異常提示不同餐抢,因此通過語言運行時來轉換”我想他表達的是這個含義)
1.在通常情況下由操作系統(tǒng)(OS)和語言的運行時(runtime)控制嗎?
- 棧: 棧通常是
后進先出
的方式預留空間. 這么做可以使得跟蹤堆棧, 變得簡單; 從棧中釋放塊只不過是指針的偏移而已. - 堆: 從堆上分配沒有固定的模式; 你可以在任何時候分配它和釋放它. 這就使得跟蹤哪部分堆已經被分配和被釋放變的異常復雜;
每一個線程都有一個棧, 但是每個應用程序通常都只有一個堆.
答:
- 當線程創(chuàng)建的時候, 操作系統(tǒng)為每一個系統(tǒng)級的線程分配
棧
, 通常情況下, 操作系統(tǒng)通過調用語言的運行時去為應用程序分配堆.
- 當線程創(chuàng)建的時候, 操作系統(tǒng)為每一個系統(tǒng)級的線程分配
- 棧附屬于線程, 當線程結束時棧被回收. 堆通常通過運行時在應用程序啟動時被分配, 當應用程序(進程)退出時被回收.
- 當
線程
被創(chuàng)建的時候, 設置棧
的大小. 在應用程序
啟動的時候, 設置堆
的大小, 但是可以再需要的時候擴展.
- 當
- 4.棧比堆要快; 因為棧存取模式使它可以輕松的分配和重新分配內存(指針只是進行簡單的遞增或遞減運算). 而對在分配和釋放的時候有更多的復雜的操作. 另外棧上的每個字節(jié)頻繁的被復用也就意味著它可能映射到處理器緩存中,使用很快(局部性原理)
2. 哪個更快低匙?
你問題的答案是依賴于實現(xiàn)的旷痕,根據不同的編譯器和處理器架構而不同。下面簡單的解釋一下:
- 棧和堆都是用來從底層操作系統(tǒng)中獲取內存的顽冶。
- 在多線程環(huán)境下每一個線程都可以有他自己完全的獨立的棧欺抗,但是他們共享堆。并行存取被堆控制而不是棧强重。