本節是操作系統系列教程的第三篇文章,屬于操作系統第一章即基礎篇,在真正開始操作系統相關章節前在這一部分回顧一些重要的主題,以下是目錄,由于本文篇幅較多因此會按上篇、中篇、下篇三次發布,目錄中黑體為本篇內容。
什么是內存
C/C++內存模型
**堆區與棧區的本質
**
**
**Java內存模型
**
**Jave中的堆區與棧區是如何實現的
**
Python內存模型
指針與引用
進程的內存模型
幻想大師-操作系統
總結
堆與棧的本質是什么
在編程語言中,堆區和棧區本質上都是內存,因此二者在本質上沒有任何區別,只不過這兩塊內存的使用方式是不一樣的。
在數據結構與算法中,我們也有堆和棧的概念,但那里指的不是內存,而是兩種數據結構。
你可能會想,我們為什么要費盡心力的提出堆和棧這兩個概念呢?之所以需要區分兩種內存用法,根源在于: 內存是有限的 。
如果計算機內存是無限的,那么我們根本就不用這么麻煩的給內存劃分兩個區域,在其中的一個區域中這樣使用內存,另一區域那樣使用內存,這些都是不需要的。即使在今天PC內存普遍都在8G、16G,這依然是不夠的,因此我們需要合理的來安排內存的使用,堆和棧就是為達到這一目的而采用的技術。
你會發現棧其實是一種非常巧妙的內存使用方法。函數調用完成后,函數運行過程中占用的內存就會被釋放掉,這樣,只要程序員代碼寫的合理(棧幀不至于過大),那我們程序就可以一直運行下去,而不會出現內存不足的現象。程序員在棧區不需要擔心內存分配釋放問題,因為這一切都是自動進行的。而如果程序員想自己控制內存,那么可以選擇在堆上進行內存分配。因此這里提供了兩種選擇,一種是“自動的”,一種是“手動的”,目的都是在合理使用內存的同時提供給程序員最大的靈活性。
堆和棧是計算機科學中很優秀的設計思想,這種設計思想充分的體現了計算機如何合理且靈活的使用有限資源。
堆區和棧區對C/C++程序員來說就是實實在在的內存,而對于Java、Python等語言的程序員來說又該如何理解內存呢?
Java、Python等內存模型
當Java、Python等語言的程序在執行時其解釋器的內存布局同樣如下圖所示,我們之前講過,解釋器也是一個C/C++程序,因此這里的代碼段包含的是解釋器的實現代碼而不是Java、Python等代碼,這一點大家一定要注意。
"C/C++程序員面對的是實實在在的物理內存,Java、Python等程序面對的是解釋器。"
C/C++分配內存是直接在物理內存中進行的,而Java、Python等程序是將內存分配請求交給解釋器,解釋器再去物理內存上進行分配。希望大家務必理解這一點。
Java、Python等程序員是看不到如下圖所示的內存布局的,因為這一切都是解釋器才能看到的,解釋器對Java、Python等程序員屏蔽了這些。Java、Python等程序員也無需關心解釋器的內存布局。
Java、Python等程序的一大優點就是內存的自動化管理,而C/C++程序員需要自己來管理從堆上分配的內存。內存管理這一項工作在Java、Python等程序中被解釋器接管了,解釋器的這項功能被稱為“垃圾回收器”。
在非C/C++語言中,我們來看兩個有代表性的語言,首先我們看一下Java。
Java內存模型
Java的內存模型中同樣有棧和堆這樣的概念,如下圖所示,在Java函數中我們定義的內置數據類型比如int a = 0,是直接存放在棧上的,引用類型,也就是用new關鍵字定義的變量是分配在堆上的。和C/C++一樣,每個Java函數在執行時都有自己的棧幀。隨著函數的調用,棧不斷的擴大。當函數調用完畢后棧幀被回收,在堆上分配的變量依然可以被后續函數使用。Java程序員無需像C/C++程序員一樣需要關心內存回收的問題,這一切都是Java的解釋器JVM來管理的。
在用法上Java中的堆和棧和C/C++是一樣的,只不過Java程序員無需關心內存的釋放問題。但是好奇的同學可能會問,C/C++中的堆和棧我已經清楚了,因為C/C++程序運行時在內存中的樣子已經在《C/C++內存模型》這一小節中詳細的講述了,那么Java中的堆和棧在內存中是什么樣子的呢,就是和上圖一樣嗎?要回答這個問題,就要涉及到Java中的堆和棧是如何實現的。
Java中的堆和棧是如何實現的
如果你自己設計過一門語言的話,你應該會很清楚這個問題。
我們先回答上一節中提到的問題,那就是Java中的堆和棧就是如上圖所示的那樣嗎?是這樣的,作為Java程序員在寫代碼時腦海里有上面這張圖基本上就夠用了。但是,Java中的堆和棧不同于C/C++當中的堆和棧。
我們已經知道Java中的內存管理其實是解釋器JVM來搞定的,作為C/C++程序,JVM的內存布局就如下圖所示。
一般情況下,當JVM運行一個Java函數時需要在堆上創建出Java函數的棧幀,然后把這些棧幀放入棧中(這里的棧指的是具有先進后出性質的數據結構)。希望大家不要被這句話繞暈,這里出現了兩個“棧”,但是含義完全不同。
-
Java棧幀:指的是上圖中我們看到的棧。
-
棧幀放入到棧:我們在數據結構課程中都學過棧,棧有push和pop兩種操作,把棧幀放入棧指的是把棧幀push到JVM所持有的棧這種數據結構當中,以此來模擬C/C++程序執行過程中函數棧幀先進后出的這種性質,當一個Java函數被執行完畢后,JVM pop掉該函數的棧幀。
如果你想在代碼級別來理解這個過程,大體上可以參考下面的代碼,注意JVM是C/C++程序,這里的代碼是一個極其簡單的描述。你可以看到如何組織棧幀完全是JVM設計者來決定的,只要棧幀具備先進后出的性質就可以。
void RunJavaFunction(JVM* jvm, string javaFunction) {
// 在堆上申請一塊空間,用于存放java棧幀
stackFrame* frame = (stackFrame*) malloc(sizeof(stackFrame));
// 把要使用的棧幀push到JVM的函數調用棧中
jvm->stack->push(frame);
// 在申請的棧幀上執行Java函數
run(javaFunction, frame);
// 執行完畢后pop掉該函數棧幀
jvm->stack->pop();
}
JVM會在自己的堆中為用new修飾的對象創建內存,這里的堆就是如上圖所示的堆,是可以要記住JVM是一個C/C++程序,JVM看到的堆才是如上圖所示的那樣。所以你會發現,一般情況下, Java中的棧和Java對象都是JVM在自己的堆上分配出來的 ,這就是Java中堆和棧是如何實現的。
在講解完Java的內存模型后,我們來看一下Python的。
Python內存模型
Python的內存模型和Java其實是類似的,Java程序員腦海中的那張圖同樣適用于Python程序員。
Python語言中的解釋器比較多,比如CPython,PyPy等,在這里我們以Python默認的解釋器CPython為例來說明,我們已經知道了解釋器其實也是一個C程序,CPython也不例外,下圖左側就是我們已經熟悉的C/C++內存布局,我們把堆區放大,如下圖右側所示。我們可以看到Python的解釋器把自己的堆區劃分成了兩部分,分別是Object-specific memory區域,以及Python core區域:
Object-specific memory這個區域專門用來存放PyObject。你也許已經知道了,Python中所有的數據類型比如int,dict,str等都是一個對象,叫做PyObject。當我們在Python中創建一個變量比如dict時,CPython就會在堆區的上半部分(Object-specific memory)中分配一塊內存,創建一個PyObject,這個PyObject用來存放我們的dict。
Python core:所有非PyObject的內存請求都在這里分配的。
所以你會發現,Python中所有的內存同樣是解釋器在自己的堆上分配的。
-
JAVA
+關注
關注
19文章
2958瀏覽量
104548 -
堆棧
+關注
關注
0文章
182瀏覽量
19732 -
python
+關注
關注
56文章
4782瀏覽量
84453
發布評論請先 登錄
相關推薦
評論