上次我們了解了最靠近CPU寄存器的緩存——高速緩存繁成。
這次我們?cè)賮砹私庀虏僮飨到y(tǒng)對(duì)于主內(nèi)存的映射管理和JVM虛擬機(jī)與之相關(guān)的一些知識(shí)洁段,這就是大名鼎鼎的虛擬存儲(chǔ)器贮缕。
什么是虛擬存儲(chǔ)器
當(dāng)CPU寄存器請(qǐng)求一個(gè)地址的數(shù)據(jù)時(shí),會(huì)依次訪問寄存器鉴扫,高速緩存身害,直到主內(nèi)存味悄,磁盤等其他IO設(shè)備。而和高速緩存一樣塌鸯,主內(nèi)存是被操作系統(tǒng)中的多個(gè)進(jìn)程所共享的侍瑟。當(dāng)然操作系統(tǒng)為了安全性,核心內(nèi)存和多個(gè)進(jìn)程之間的內(nèi)存是相互獨(dú)立開的丙猬。
這樣為了更有效的管理存儲(chǔ)器涨颜,現(xiàn)代操作系統(tǒng)對(duì)于主內(nèi)存上建立了一種抽象的概念,就是虛擬存儲(chǔ)器(VM)茧球。VM為每個(gè)進(jìn)程提供了一個(gè)大的庭瑰,完整的,私有的地址空間抢埋,映射到了物理存儲(chǔ)之上(包括主內(nèi)存和作為交換分區(qū)的部分磁盤存儲(chǔ))弹灭。它主要提供了三個(gè)能力:
- 它將主存看為磁盤空間上的地址空間的高速緩存,并通過良好的過期機(jī)制保證良好的局部性
- 為每個(gè)進(jìn)程提供了一致的地址空間羹令,屏蔽了實(shí)際的物理地址
- 保護(hù)每個(gè)進(jìn)程的地址空間不被其他進(jìn)程所破壞
虛擬存儲(chǔ)器的好處
- 簡(jiǎn)化鏈接 VM使每個(gè)進(jìn)程都可以認(rèn)為自己訪問一致的全部的地址空間鲤屡,大大簡(jiǎn)化了編程復(fù)雜度,并且屏蔽了不同進(jìn)程間的內(nèi)存福侈,提升安全性酒来。
- 擴(kuò)展物理內(nèi)存 VM可以是實(shí)際物理內(nèi)存的許多倍,因?yàn)殡m然操作系統(tǒng)可以同時(shí)運(yùn)行很多進(jìn)程肪凛,但同時(shí)活動(dòng)的進(jìn)程確實(shí)有限的堰汉。這樣VM提供自動(dòng)化的內(nèi)存片段的失效和加載能力,提升了系統(tǒng)負(fù)載伟墙。
- 簡(jiǎn)化存儲(chǔ)器分配 對(duì)于每個(gè)進(jìn)程來說翘鸭,所能訪問的地址空間都是連續(xù)的,但實(shí)際上主內(nèi)存和物理內(nèi)存上的分片卻可以是零散的戳葵,所有的映射和分配工作都由虛擬存儲(chǔ)器屏蔽掉了就乓。
虛擬存儲(chǔ)器的結(jié)構(gòu)
32位操作系統(tǒng)最大支持2^32 的地址空間也就是4GB,而64位操作系統(tǒng)最大支持2^64 的地址空間拱烁,也就是理論上17179869184GB的地址空間生蚁。
當(dāng)然,一般物理內(nèi)存是有限的戏自,而虛擬存儲(chǔ)器卻可以映射更多的地址空間邦投。
計(jì)算機(jī)系統(tǒng)在實(shí)際的物理存儲(chǔ)器和磁盤存儲(chǔ)器之上,建立了一層映射擅笔,叫做PTE(page table entry)頁(yè)表?xiàng)l目志衣。PTE就像一個(gè)索引表屯援,將每個(gè)虛擬地址,映射到相應(yīng)的物理存儲(chǔ)器或者磁盤存儲(chǔ)器之上念脯。計(jì)算機(jī)系統(tǒng)并不是按照每個(gè)地址所對(duì)應(yīng)的字(4字節(jié))來管理內(nèi)存的狞洋,而是將其劃分為不同的頁(yè)page來管理,一個(gè)頁(yè)一般來說在4KB~2MB和二。而因?yàn)橹鲀?nèi)存和磁盤直接巨大的性能差距徘铝,所以當(dāng)數(shù)據(jù)更新時(shí),都采取寫回的方式而不是直寫惯吕。
以下是PTE和頁(yè)表的關(guān)系結(jié)構(gòu):
其中有效位代表是否在主內(nèi)存之中惕它,如果存在直接訪問主內(nèi)存中的地址,如果不存在則需要訪問磁盤废登,并將其刷新至主內(nèi)存之中淹魄。
CPU中都有專門的MMU模塊來訪問PTE,將虛擬地址轉(zhuǎn)化為物理地址進(jìn)行訪問堡距。
而我們可以設(shè)想這種訪問量是非常巨大的甲锡,所以PTE的加載和訪問性能就特別重要,為此羽戒,CPU又建立專門的TLB(Translation Lookaside Buffer)翻譯后備緩沖器來進(jìn)行PTE的高速緩存缤沦,其作用和原理于L1,L2高速緩存基本類似易稠,如下是一個(gè)用來訪問TLB的虛擬地址的組成部分缸废,用來在TLB高速緩存中查詢:
我們?cè)賮砑僭O(shè)如果只有一個(gè)PTE索引的話,那么就算我們只有32位的地址空間驶社,4KB的頁(yè)和4字節(jié)的PTE企量,那么也總是需要4MB的頁(yè)表保留在存儲(chǔ)器中的,而有可能其中的大部分都是空亡电。而如果是64位系統(tǒng)的話届巩,那么將花費(fèi)非常大的空間來進(jìn)行存儲(chǔ)。
為了解決這個(gè)問題份乒,需要建立多級(jí)頁(yè)表恕汇,例如第一級(jí)頁(yè)表每個(gè)PTE映射4MB的片,每個(gè)片又由1024個(gè)4KB的片組成或辖,從而映射到二級(jí)頁(yè)表拇勃,再最終映射到物理存儲(chǔ)器。這樣當(dāng)?shù)谝患?jí)頁(yè)表為空時(shí)孝凌,二級(jí)頁(yè)表中的1024個(gè)片就完全不用存在,從而節(jié)約了大量的緩存空間:
需要注意的是月腋,每個(gè)進(jìn)程都享有獨(dú)立的PTE蟀架,這樣就做到了每個(gè)進(jìn)程都可以訪問一致的瓣赂,完整的,連續(xù)的地址空間并能夠相互隔離片拍。
而對(duì)于實(shí)際的Linux操作系統(tǒng)來說,頁(yè)表的層級(jí)更多:32bit的Linux采用三級(jí)映射:PGD-->PMD-->PTE,64bit的Linux采用四級(jí)映射:PGD-->PUD-->PMD-->PTE者疤,多了個(gè)PUD(因?yàn)?4位管理的內(nèi)存地址更多):
同時(shí)Linux也為每個(gè)進(jìn)程分配獨(dú)立的虛擬空間地址task_struct:
JVM虛擬機(jī)對(duì)于虛擬存儲(chǔ)器的使用
好了互订,我們了解了有關(guān)于虛擬存儲(chǔ)器的大概知識(shí)后再來看看其與之JVM虛擬機(jī)之間的關(guān)系。
首先和其他所以運(yùn)行在操作系統(tǒng)中的進(jìn)程一樣纲缓,JVM也是通過虛擬內(nèi)存來進(jìn)行內(nèi)存的訪問的卷拘,而其中的頁(yè)的切換命中過期等操作,也都是完全由操作系統(tǒng)屏蔽掉的祝高,是我們基本不用關(guān)心的部分栗弟。
但就算如此,因?yàn)镴VM中其實(shí)也是有著大量關(guān)于虛擬機(jī)內(nèi)存的管理邏輯工闺,所以在其垃圾回收部分乍赫,也巧妙的利用了虛擬內(nèi)存的特性,有效的提升了性能陆蟆。
我們?cè)O(shè)想一個(gè)64位的操作系統(tǒng)雷厂,能夠最大管理17179869184GB的地址空間,但實(shí)際上絕大部分時(shí)候叠殷,我們是利用不到這么多的地址的改鲫。所以在最新的ZGC垃圾回收期中,JVM使用了染色指針技術(shù)溪猿,也就是將64位地址的高18位用來做對(duì)象的特殊標(biāo)記钩杰,而剩下的46位作為內(nèi)存地址。這樣ZGC所能管理的最大內(nèi)存就不能超過4TB了诊县,但對(duì)于我們來說也是遠(yuǎn)遠(yuǎn)夠用的讲弄。
那么ZGC用18位的標(biāo)記來做什么呢?我們先來簡(jiǎn)單了解一下垃圾回收的知識(shí)依痊。
JVM采用根遍歷的方式來做垃圾回收避除,也就是找到若干的GCRoots,再循環(huán)遍歷所有的關(guān)聯(lián)的對(duì)象圖譜胸嘁,將其中沒有和GCRoots關(guān)聯(lián)到的對(duì)象進(jìn)行回收瓶摆。而又可以分為標(biāo)記清除,標(biāo)記復(fù)制性宏,標(biāo)記整理三類回收算法群井。
這里不對(duì)JVM的垃圾回收再展開討論,只是需要注意的是毫胜,不管在標(biāo)記的過程中书斜,還是JVM運(yùn)行的過程中诬辈,為了最終成功的進(jìn)行回收,除了對(duì)象本身外荐吉,都還要記錄例如對(duì)象的分代年齡焙糟,引用標(biāo)記,是否被移動(dòng)等一系列額外信息样屠。在除了ZGC的垃圾回收方式外穿撮,其他例如CMS,G1和Serial都等是利用了對(duì)象本身的Markword對(duì)象頭或者索引表來記錄這些信息的痪欲。這樣就產(chǎn)生了如下缺點(diǎn):
- JVM中需要使用20%~20%的額外空間來存儲(chǔ)引用標(biāo)記和索引信息
- 對(duì)象修改時(shí)要建立額外的寫屏障來更新維護(hù)標(biāo)記和索引信息悦穿,更多的指令降低性能
- 訪問對(duì)象時(shí)可能產(chǎn)生額外的路由信息(例如對(duì)象在不同的塊中被轉(zhuǎn)移)而產(chǎn)生多次尋指訪問操作
而為了解決這些問題ZGC將關(guān)于對(duì)象的標(biāo)記直接存儲(chǔ)在其虛擬內(nèi)存地址上,這樣在Linux操作系統(tǒng)的多重映射技術(shù)支持下勤揩,可以將多個(gè)帶有標(biāo)記的虛擬內(nèi)存咧党,截?cái)鄻?biāo)志位后再映射到同一段物理地址上,同時(shí)截?cái)嗟牟糠忠材芸焖僮x出對(duì)象的標(biāo)記信息陨亡,從而大大降低了內(nèi)存的使用以及提升了訪問效率傍衡。
參考資料:
《深入理解計(jì)算機(jī)系統(tǒng)》
《深入理解JAVA虛擬機(jī)》
《Linux服務(wù)器性能調(diào)整》