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