1. 程式人生 > 其它 >【博學谷學習記錄】超強總結,用心分享|Java基礎分享-計算機記憶體管理

【博學谷學習記錄】超強總結,用心分享|Java基礎分享-計算機記憶體管理

目錄

 

一、前言

二、馮·諾伊曼結構

1、早期計算機結構

2、理論提出

3、五大部件

三、儲存器

1、儲存器的種類

2、摩爾定律

3、儲存層次

4、關於記憶體分層的思考

四、記憶體管理的需求和實現

1、需求確認

2、實現方式

五、程式重定位

1、地址空間

2、連結階段

3、載入階段

4、地址空間的讀取過程

一、前言

學妹剛上大學,問我計算機記憶體知識需要了解麼?我當場就是傻瓜警告,於是就有了這篇文章。

為什麼要去了解記憶體知識?因為它是計算機作業系統中的核心功能之一,各高階語言在進行記憶體的使用和管理上,無一不依託於此底層實現,比如我們熟悉的Java記憶體模型。

最近幾篇文章學習作業系統的記憶體管理後,喜歡底層的同學可以去學習 CPU 結構、機器語言指令和程式執行相關的知識,而看重實用性的同學後續學習多程序多執行緒和資料一致性時,可以有更深刻的理解。

二、馮·諾伊曼結構

1、早期計算機結構

在馮·諾依曼結構提出之前的計算機,是一種計算機只能完成一種功能,編輯好的程式是直接整合在計算機電路中,例如一個計算器僅有固定的數學計算程式,它不能拿來當作文書處理軟體,更不能拿來玩遊戲。若想要改變此機器的程式,你必須更改線路、更改結構甚至重新設計此計算機。

簡單來說,早期的計算機是來執行一個事先整合在電路板上的某一特定的程式,一旦需要修改程式功能,就要重新組裝電路板,所以早期的計算機程式是硬體化的。

2、理論提出

1945年,馮·諾依曼由於在曼哈頓工程中需要大量的運算,從而使用了當時最先進的兩臺計算機 Mark I 和 ENIAC,在使用 Mark I 和 ENIAC 的過程中,他意識到了儲存程式的重要性,從而提出了“儲存程式”的計算機設計理念,即將計算機指令進行編碼後儲存在計算機的儲存器中,需要的時候可以順序地執行程式程式碼,從而控制計算機執行,這就是馮.諾依曼計算機體系的開端。

這是對計算機發展有深刻意義的重要理論,從此我們開始將程式和資料一樣看待,程式也在儲存器中讀取,這樣計算機就可以不單單隻能執行事先編輯整合在電路板上的程式了,程式由此脫離硬體變為可程式設計的了,而後誕生程式設計師這個職業。

關於馮·諾依曼這位大神,值得單獨開一篇文章來聊聊。

3、五大部件

馮諾依曼計算機體系結構如下:

馮·諾依曼結構用極高的抽象描述了計算器的五大部件,以及程式執行時資料和指令的流轉過程。現在快速的將現代計算機的設計概念結構瞭解後,我們把目光聚焦在本文的主角——儲存器上。

三、儲存器

1、儲存器的種類

我們編寫的程式、下載的電影,自然需要有個地方存放這些資料,儲存器現在主要有易失性儲存器和非易失性儲存器兩種。存取速度上來看,前者要快很多。資料持久化上來看,當電源供應中斷後,易失性儲存器所儲存的資料便會消失,而非易失性儲存器所儲存的資料並不會消失,重新供電後,就能夠讀取儲存器中的資料。

易失性儲存器也叫隨機儲存儲存器,分為動態隨機儲存儲存器和靜態隨機儲存儲存器,表現出來的區別在速度上。

動態隨機儲存儲存器,英文縮寫寫作DRAM,一般每個單元由一個電晶體和一個電容組成。

特點是單元佔用資源和空間小,速度比SRAM慢,需要重新整理。一般計算機記憶體即由DRAM組成。在PC上,DRAM以記憶體條的方式出現。

靜態隨機儲存儲存器,英文縮寫寫作SRAM,一般每個單元由6個電晶體組成,但近來也出現由8個電晶體構成的SRAM單元。

特點是速度快價格貴,單元佔用資源比DRAM多,在PC上,一般CPU和GPU的快取即由SRAM構成。

2、摩爾定律

摩爾定律(英語:Moore's law)是由英特爾(Intel)創始人之一戈登·摩爾提出的。

其內容為:積體電路上可容納的電晶體數目,約每隔兩年便會增加一倍;經常被引用的“18個月”,是由英特爾執行長大衛·豪斯(David House)提出:預計18個月會將晶片的效能提高一倍(即更多的電晶體使其更快),是一種以倍數增長的觀測。

上世紀末開始,隨著CPU的效能極速發展,矛盾出現了。CPU執行的太快了,而磁碟的資料讀取又太慢了。

經過上一節的介紹,我們發現並沒有一種可以同時滿足穩定可用、支援持久化儲存、存取速度要非常快、成本便宜並且容量大體積小的儲存器,那就各取其長,將各類儲存器,根據其各自的特點,按一定規律組合起來使用。

後來,CPU的效能達到物理極限而引出的計算機多核多級快取架構,本文暫不展開。

3、儲存層次

百科如是說明:儲存層次是在計算機體系結構下儲存系統層次結構的排列順序。每一層於下一層相比都擁有較高的速度和較低延遲性,以及較小的容量。大部分現今的中央處理器的速度都非常的快。

大部分程式工作量需要儲存器訪問。由於快取記憶體的效率和儲存器傳輸位於層次結構中的不同等級,所以實際上會限制處理的速度,導致中央處理器花費大量的時間等待儲存器I/O完成工作。

簡單來說,如果單一儲存器無法滿足全部的需求,那就通過各類儲存器的組合使用,在一定程度上滿足了各方面需求都達標的一種儲存結構,即儲存層次,或稱記憶體的層次結構。

在網上找了這張經典的記憶體層次結構圖

大部分電腦中的儲存層次如下四層:

  • 暫存器:(應該是)最快的訪問。
  • 快取記憶體(L1-L3:SRAM):快取記憶體中的訪問速度為納秒級別,非常快。

第一級快取記憶體(L1),通常訪問只需要幾個週期,通常是幾十個KB。

第二級快取記憶體(L2),比L1約有2到10倍較高延遲性,通常是幾百個KB或更多。

第三級快取記憶體(L3)不一定有,比L2更高的延遲性,通常有數MB之大。

第四級快取記憶體(L4)不普遍,CPU外部的DRAM,但速度較主存高。

  • 主存(DRAM):訪問需要幾百個週期,可以大到數幾十GB。
  • 磁碟儲存:需要成千上百個週期,容量非常大,持久化儲存資料。

這一節中介紹的幾種儲存器,其各自工作所在的層次一目瞭然,需要注意的是其中快取記憶體不受作業系統管理,我們重點看作業系統可管理的主存,後文為了方便敘述,不做特殊說明時,記憶體單指上圖中的主存。

如今,市面上個人計算機的常規配置是三級快取記憶體下配上16G記憶體和512G的固態硬碟,各層儲存器在作業系統的記憶體管理下協調工作,讓我們“同一時間”可以流暢使用多個軟體。

4、關於記憶體分層的思考

計算機為了提高訪問速度而採用的記憶體分層結構,似乎像是網際網路公司高效能架構中的常用手段——快取,其一我們把資料儘可能的放在靠近使用者(CPU)的地方,其二我們使用速度更快但不會持久化資料的中介軟體來做資料庫(磁碟)的快取(記憶體)。

這就是要去學習基礎知識和閱讀經典論文的原因,我們會發現思路都是相通的,落地方案的差異也不過是考慮到了特定領域與業務。

四、記憶體管理的需求和實現

1、需求確認

現在,基於以上記憶體的硬體結構,我們作業系統要完成對記憶體管理的能力,它主要應該具備如下的能力:

  • 抽象:邏輯地址空間,遮蔽掉真實的記憶體地址,在多道程式環境下,程式中的邏輯地址與記憶體中的實體地址不可能一致,因此儲存管理必須提供地址變換功能,把邏輯地址轉換成相應的實體地址。
  • 分配與回收:即記憶體空間的高效率分配和及時回收。
  • 安全與隔離:獨立地址空間,保護各自程序的保護,惡意去破壞其他程序的資料,隔離性,保證各道作業在各自的儲存空間內執行,互不干擾。
  • 共享:訪問相同記憶體,各程序間的資料傳遞機制允許多個程序訪問同一塊公共的記憶體空間,這是效率最高的程序間通訊形式。原本每個程序的記憶體地址空間都是相互隔離的,但作業系統提供了讓程序主動建立、對映、分離、控制某一塊記憶體的程式介面。當一塊記憶體被多程序共享時,各個程序往往會與其它通訊機制,譬如訊號量結合使用,來達到程序間同步及互斥的協調操作。
  • 虛擬化:在無需擴大記憶體硬體容量的情況下,為了滿足同時執行多個程式,我們需要更大的地址空間,也就是虛擬記憶體。

2、實現方式

作業系統主要是通過下面五種方法實現的:

  • 程式重定位
  • 分段
  • 分頁
  • 虛擬記憶體
  • 按需分頁虛擬記憶體

這裡涉及到的實現方式我們後面會逐一學習,現在先來看程式重定位。

五、程式重定位

1、地址空間

首先,需要了解兩種地址空間,實體地址空間和邏輯地址空間,前者是硬體支援的地址空間,也叫絕對地址。

後者是程式看到的,一個執行的程式所擁有的記憶體範圍,也叫相對地址。

作業系統負責建立並維護這個對應關係,我們知道程式的執行,依次需要經過編譯、連結、載入(程式重定位)後,才能執行。

其中編譯階段,簡單來說就是由“高階語言”轉成“機器語言”的過程,該階段和地址空間關係不大,這裡暫時不展開,我們重點看後兩個階段。

2、連結階段

經過連結階段後,程式就是擁有完整的邏輯地址空間的可以執行的程式了,比如Windows下的.exe 檔案。

三種連結方式:

  • 靜態連結:在程式執行之前,先將各目標模組及它們所需的庫函式連線成一個完整的可執行檔案(裝入模組) ,之後不再拆開。
  • 裝入時動態連結:將各目標模組裝入記憶體時,邊裝入邊連結的連結方式。
  • 執行時動態連結:在程式執行中需要該目標模組時,才對它進行連結。

3、載入階段

載入階段,或稱之為裝入階段,由作業系統進行記憶體地址分配,並將程式的邏輯地址轉換為實體地址。

這塊同樣有三種方式進行載入:

  • 絕對裝入:在之前的編譯階段,編譯程式直接產生絕對地址的目的碼。而後載入程式按照載入模組中的地址,將程式和資料裝入記憶體。那哪種程式環境,可以在編譯階段就能確認實體地址呢?沒錯,絕對裝入只適用於單道程式環境。
  • 靜態重定位:編譯和連結後的指令中使用的地址、資料存放的地址都是相對於起始地址而言的邏輯地址,在載入階段可根據記憶體的當前情況,裝入到記憶體的適當位置。裝入時對地址根據初始位置和偏移量進行重定位,將邏輯地址變換為實體地址,地址變換是在裝入時一次完成的。也就是說,必須分配其要求的全部記憶體空間,如果沒有足夠的記憶體,就不能裝入該作業。
  • 動態重定位:現在又稱動態執行時裝入。編譯、連結後的裝入模組的地址都是從0開始的。裝入程式把裝入模組裝入記憶體後,並不會立即把邏輯地址轉換為實體地址,而是把地址轉換推遲到程式真正要執行時才進行。因此裝入記憶體後所有的地址依然是邏輯地址。這種方式需要一個重定位暫存器的支援。這種方式雖然帶來了複雜性,但是卻將記憶體空間,極大極大的擴大了(虛擬記憶體的作用之一),現在的載入程式一般都是這種方式進行載入。

4、地址空間的讀取過程

由前文的瞭解,我們可以想到 CPU 對某一地址的內容讀取,是經過如下幾步完成的:

  1. CPU需要在邏輯地址的記憶體內容;
  2. 記憶體管理單元尋找在邏輯地址和實體地址之間的對映(這裡可以做地址安全的檢查,確保程式不相互干擾);
  3. 控制器從匯流排傳送在實體地址的記憶體內容的請求;
  4. 記憶體傳送實體地址記憶體內容給CPU;