堆與棧
關於堆和棧的問題,對於熟悉C++的同學來說可能理解的比較清楚,但是由於Java的一切對象都是在堆上,因此有時候可能反而會比較迷惑,比如:
為什么要分堆和棧?
堆和棧的區別是什么?
為什么堆是線程共享的而棧不是?
很多懂一點Java的人甚至是懂一點編程的人都知道內存一般分為堆和棧,棧由系統進行關系,而堆由程序員自己管理,...balabala,基本任何一本語言基礎書都會提到這些特點,但是你有想過為什么嗎?
為什么要分堆和棧
之所以要區分堆和棧,是由於程序需要兩種不同特性的內存形似而確定的。在C++中,新建一個對象有兩種方式,靜態分配和動態分配,
靜態分配
一般來說,靜態分配用於初始化已知對象大小的時候,比如int a[10];
如果我們能夠確定這個數組是10個,我們可以使用這種方式。這種方式所需要的內存在編譯期間即可確定,因此操作系統便可以預先確定所指定大小內存給變量,並且可以在變量生命周期結束后自動釋放。
動態分配
然而在某些場景下,可能需要根據某些情況來申請內存,比如int* a =new int[count];
而變量count
可能就來自於某個配置文件或者用戶手動輸入的值。這個時候操作系統就無法再預先確定內存大小,並且不執行到new int[count]
這一行代碼的時候,是無法知道所要分配的內存大小,因此操作系統分出一塊內存,用來進行動態分配。並且規定,動態分配的內存需要由客戶端自行管理。
Java 中的堆
由於JVM規范中規定,JVM中的一切對象都存儲在堆上(內存逃逸除外)。因此在Java中並不存在對象的靜態分配,因此堆和棧的來源看似就非常理所當然。但是要明白,在操作系統中,堆和棧的出現的緣由。
堆和棧的區別
知道了為什么要區分堆和棧,再來看看堆和棧的區別。
-
堆是運行時確定內存大小,而棧在編譯時即可確定內存大小
理由便是第一節中提到的,這是區分堆和棧的初衷
-
堆內存由用戶管理(Java中由JVM管理),棧內存會被自動釋放
-
棧實現方式采用數據結構中的棧實現,具有(LIFO)的順序特點,堆為一塊一塊的內存
-
棧由於其實現方式,在分配速度上比堆快的多。分配一塊棧內存不過是簡單的移動一個指針
-
在JVM中,棧不會被程序員直接使用,程序員操作的一般都是堆。
-
棧為線程私有而堆為線程共享
雖然堆和棧有這么多的區別,但是這些區別都是由於操作系統而決定的,在硬件上,他們本質都是RAM
為什么堆是線程共享的而棧不是?
上面最后一點提到了棧為線程私有而堆為線程共享。這是為什么呢???
其實很簡單,為了解決一個問題:線程間通信。
想要實現線程間通信,目前有兩種方法:
- 消息傳遞
- 共享內存
共享內存便是我們所說的將堆設置為線程間共享的,這樣我們能夠通過堆中的對象實現數據共享,這樣便使得其他線程能夠知道某個線程修改了某個數據。但是這樣帶來的問題可能就有線程安全問題等,但是這樣做的優勢便在於速度更快和節約內存,Java,C#等使用這種方式
消息傳遞是每個線程都私有自己的數據空間,當需要線程通信的時候,便需要一個線程顯示的給另外一個線程發送具體的消息,這樣做的能夠讓多線程更加安全,但是私有的線程空間和消息傳遞帶來的是需要給內個線程都拷貝相同的對象,變量等,並且可能會帶來效率問題。而Erlang和JoCaml便是使用消息傳遞式線程通信。
尊重勞動成功,轉載注明原創
參考文章:
Why do threads share the heap space?
What’s the difference between a stack and a heap?
What and where are the stack and heap?
如果覺得寫得不錯,歡迎關注微信公眾號:逸游Java ,每天不定時發布一些有關Java進階的文章,感謝關注