認識Java底層操作系統與並發基礎
一、現代計算機硬件結構
核心部分: CPU、內存
1.CPU內部結構
- 控制單元: 整個 CPU 的指揮控制中心
- 運算單元: 運算器核心,執行算術運算與邏輯運算。運算器接收控制單元的指令而執行動作
- 存儲單元: CPU 中暫時存儲數據的地方,包括 CPU 片內緩存 Cache 和 寄存器組
1.1.CPU緩存結構
現代 CPU 為瞭提升執行效率,減少 CPU 與內存的交互(交互影響 CPU 效率),一般在 CPU上集成瞭多級緩存架構,常見的為三級緩存結構
- L1 Cache,分為數據緩存和指令緩存,邏輯核獨占
- L2 Cache,物理核獨占,邏輯核共享
- L3 Cache,所有物理核共享
此機器的三級緩存架構如下圖:L1 Cache又分為兩種,指令存儲單元(存指令),和邏輯存儲單元(存邏輯)。理論上一臺機器可以有多個 CPU,由插槽決定,一個 CPU 又有多核,一個核又可以由多個邏輯處理器。
寄存器是 CPU 內部元件,讀寫速度非常快。 CPU 讀取數據隻會從寄存器中去取,每個 CPU 都有一個獨有的寄存器,其他 CPU 無法訪問。采用寄存器,可以減少 CPU 訪問內存的次數,從而提高瞭 CPU 的工作速度。
越靠近 CPU 讀取速度越快,摩爾定律中,CPU 以每18個月翻一番的速度在發展,而內存和硬盤的發展速度遠遠跟不上。為瞭解決 CPU 運算速度和 I\O 速度不匹配的問題,CPU 開始被內置瞭少量的高速緩存 Lx Cache(CPU空間有限,存儲元件大小受限)。
- 存儲器存儲空間大小: 內存 > L3 Cache > L2 Cache > L1 Cache > 寄存器
- 存儲器讀取速度快慢: 寄存器 > L1 Cache > L2 Cache > L3 Cache > 內存
- 緩存是由最小的存儲區塊— 緩存行(CacheLine) 組成,緩存行大小通常為64byte。我的機器L1的緩存大小時512K,則由512 * 1024/64個緩存行組成。
CPU讀取存儲器數據過程: CPU 僅能直接從寄存器中獲取數據。 假設數據 x = 0 在內存中,則它的取值過程如下:
判斷寄存器中是否存在
不存在則遍歷L1 Cache 看是否存在,不存在遍歷L2 Cache,L2 Cache 中沒有,遍歷L3 Cache。中間過程存在,則會把 Cache 行鎖住,拷貝到上一級,直至到寄存器。
Cache 中沒有則區內存中找,先通知內存控制器占用總線帶寬,通知內存加鎖,發起內存讀請求,等待回應,回應數據拷貝到L3 Cache。 註意:整個過程加鎖直至到CPU才會解開
局部性原理:在CPU訪問存儲設備時,無論是存取數據還是存取指令,都趨於聚集在一片連續的區域中。
這種局部性原理又有兩種:
- 時間局部性(Temporal Locality): 如果一個信息項正在被訪問,那麼在近期它很可能還會被再次訪問。 比如循環、遞歸、方法的反復調用等。
- 空間局部性(Spatial Locality): 如果一個存儲器的位置被引用,那麼將來他附近的位置也會被引用。 比如順序執行的代碼、連續創建的兩個對象、數組等。
空間局部性的例子: 一個很大的二維數組,累加求和一行一行加會比一列一列累加快很多。在CPU 在內存中讀取數據時會將附件的數據都讀進去。
1.2.CPU運行安全等級
CPU被劃分為 4 個運行級別:
- ring0 內核態
- ring1
- ring2
- ring3 用戶態
Linux 和 Windows 都隻用到瞭兩個級別:ring0、ring3,操作系統內部內部程序指令通常運行在 ring0 級別,操作系統以外的第三方程序運行在 ring3 級別,第三方程序如果要調用操作系統內部函數功能,由於運行安全級別不夠,必須切換CPU運行狀態,從 ring3 切換到 ring0, 然後執行系統函數,創建線程,線程阻塞喚醒是重型操作,因為CPU要切換運行狀態。
JVM 創建線程是 CPU 的流程:
- 第一步:CPU 從 ring3 切換 ring0 創建線程
- 第二步: 創建完畢,CPU從 ring0 切回 ring3
- 第三步: 線程執行JVM程序
- 第四步: 線程執行完畢,銷毀切回 ring0
- 第五步: 線程銷毀,切回 ring3
2.操作系統內存管理
為瞭使程序運行安全隔離與穩定,操作系統有用戶空間
與內核空間
兩個概念。以 32位操作系統4G大小的內存空間為例:
Linux 為內核代碼和數據結構預留瞭幾個頁框,這些頁永遠不會被轉出到磁盤上(4GB內存空間,用戶程序可使用3GB)。如圖綠色部分的線性地址可由用戶代碼和內核代碼進行引用(即用戶空間)。黃色部分的線性地址隻能由內核代碼進行訪問(即內核空間)。
進程與線程隻能運行在用戶方式(usermode) 或 內核方式(kernelmode) 下。用戶程序運行在用戶方式下,而系統調用運行在內核方式下。
用戶方式下使用一般的堆棧(用戶空間的堆棧),內核方式下使用固定大小的堆棧(內核空間的堆棧,一般為一個內存頁的大小),即每個進程與線程其實有兩個堆棧,分別運行與用戶態與內核態。
CPU調度的基本單位線程,也劃分為:
- 內核線程模型(KLT): Java使用,內核保存線程的狀態和上下文信息,線程阻塞不會引起進程阻塞。在多處理器系統上,多線程在多處理器上並行運行。線程的創建、調度和管理由內核完成,效率比ULT要慢,比進程操作快。
- 用戶線程模型(ULT): 不依賴操作系統核心,應用提供創建、同步、調度和管理線程的函數來控制用戶線程。不需要用戶態/內核態切換,速度快。內核對ULT無感知,線程阻塞則進程(包括它的所有線程)阻塞
線程都有兩個堆棧,一個在用戶空間,一個在內核空間。阻塞、創建、殺死線程將拋棄用戶空間的堆棧,轉移到內核空間,執行完畢後再轉移到用戶空間。
3.進程與線程
進程: 操作系統資源分配的最小單位,例如:啟動一個 Java 程序,操作系統就會創建一個Java 進程,進程中可以包含多個線程。
線程: 操作系統調度CPU的最小單元,線程都擁有各自的計數器、堆棧和局部變量等屬性, 並且能夠訪問共享的內存變量。CPU 在這些線程上高速切換,讓使用者感覺到這些線程在同時執行(並發)。
線程上下切換: 保存上一個線程運行的中間狀態,執行下一個線程
- 串行: 時間上不可重疊,前一個任務沒完成,下一個任務隻能等待
- 並行: 時間上是重疊的,兩個任務在同一時刻互不幹擾的同時執行
- 並發: 運行兩個任務彼此幹擾,同一時間點,隻有一個任務執行,交替執行
到此這篇關於認識Java底層操作系統與並發基礎的文章就介紹到這瞭,更多相關Java底層操作系統內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Linux內存管理和尋址詳細介紹
- Java內存模型JMM與volatile
- 一種新的日期處理方式之JavaScript Temporal API
- Vue自定義鈴聲提示音組件的實現
- 一篇文章帶你入門Java基本概念