1. 程式人生 > 其它 >《Redis設計與實現》讀書筆記(三十三) ——Redis排序命令sort的實現

《Redis設計與實現》讀書筆記(三十三) ——Redis排序命令sort的實現

JVM面試題

java程式碼執行過程

Java原始檔——>編譯器——>位元組碼檔案——>jvm——>機器碼,如下步驟:

  • 程式碼編譯為class檔案(指令:javac)
  • 裝載class(類載入器:ClassLoader)
  • 執行class(解釋執行、編譯執行)

類載入器

  • 啟動類載入器:負責載入 JAVA_HOME\lib 目錄中的,或通過-Xbootclasspath 引數指定路徑中的,且被虛擬機器認可(按檔名識別,如 rt.jar)的類
  • 擴充套件類載入器:負責載入 JAVA_HOME\lib\ext 目錄中的,或通過 java.ext.dirs 系統變數指定路徑中的類庫
  • 應用程式類載入器:負責載入使用者路徑(classpath)上的類庫

類載入機制-雙親委派模型

某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入

使用雙親委派模型的好處在於Java類隨著它的類載入器一起具備了一種帶有優先順序的層次關係

jvm記憶體空間劃分

方法區

執行緒共享區域,即我們常說的永久代(Permanent Generation),用於儲存被 JVM 載入的類資訊常量靜態變數即時編譯器編譯後的程式碼等資料,執行時常量池(Runtime Constant Pool)是方法區的一部分

執行緒共享區域,建立的物件和陣列都儲存在 Java 堆記憶體中,也是垃圾收集器進行垃圾收集的最重要的記憶體區域

Java 堆從 GC 的角度還可以細分為:新生代和老年代

  • 新生代:是用來存放新生的物件一般佔據堆的 1/3 空間;由於頻繁建立物件,所以新生代會頻繁觸發MinorGC 進行垃圾回收
  • 老年代:主要存放應用程式中生命週期長的記憶體物件

虛擬機器棧

執行緒私有區域,描述java方法執行的記憶體模型;一個執行緒中,每呼叫一個方法建立一個棧幀,用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊;每一個方法從呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中入棧到出棧的過程(棧幀隨著方法呼叫而建立,隨著方法結束而銷燬);棧中可能會引發的異常:

  • 執行緒請求的棧深度大於jvm所允許的深度-StackOverflowError
  • 無法申請到足夠記憶體-OutOfMemoryError

本地方法棧

執行緒私有區域,本地方法棧和 Java Stack 作用類似, 區別是虛擬機器棧為執行 Java 方法服務,而本地方法棧則為Native 方法服務

pc暫存器

執行緒私有區域,指向虛擬機器位元組碼指令的位置,唯一一個無OOM的區域

執行緒私有資料區域生命週期與執行緒相同, 依賴使用者執行緒的啟動/結束;執行緒共享區域隨虛擬機器的啟動/關閉而建立/銷燬

垃圾回收演算法

標記-清除

標記清除法是先找到記憶體裡的存活物件並對其進行標記,然後統一把未標記的物件統一的清理

優點:

簡單直接,速度也非常塊,適合存活物件多,需要回收的物件少的場景

缺點:

會造成不連續的記憶體空間:清除後記憶體會有很多不連續的空間,這也就是我們常說的空間碎片,這樣的空間碎片太多不僅不利於我們下次分配,而且當有大物件建立的時候,我們明明有可以容納的總空間,但是空間都不是連續的造成物件無法分配,從而不得不提前觸發GC

效能不穩定:記憶體中需要回收的物件,當記憶體中大量物件都是需要回收的時候,通常這些物件可能比較分散,所以清除的過程會比較耗時,這個時候清理的速度就會比較慢了

使用場景:只有小部分物件需要進行回收的,所以標記清除法比較適用於老年代的垃圾回收,因為老年代一般存活物件會比回收物件要多

標記-複製

將記憶體分為大小相同的兩塊,每次使用其中的一塊。當這一塊記憶體使用完之後,就將存活物件複製到另一塊記憶體中;然後再把使用的記憶體一次清理掉,這樣就使每次記憶體回收都是對記憶體空間的一半進行回收

優點:

解決了標記清除演算法的空間碎片問題

缺點:

會浪費一部分空間:總是會有一塊空閒的記憶體區域是利用不到的,這也造成了資源的浪費

存活物件多會非常耗時:因為複製移動物件的過程是比較耗時的,不僅需要移動物件本身還需要修改使用了這些物件的引用地址,所以當存活物件多的場景會非常耗時

需要擔保機制:因為複製區總會有一塊空間的浪費,而為了減少浪費空間太多,所以我們會把複製區的空間分配控制在很小的區間,但是空間太小又會產生一個問題,就是在存活的物件比較多的時候,這時複製區的空間可能不夠容納這些物件,這時就需要借一些空間來保證容納這些物件,這種從其他地方借記憶體的方式我們稱它為擔保機制

適用場景:比較適合存活物件較少的場景,這也正是新生代物件的特點,所以一般新生代的垃圾回收器基本都會選擇標記複製法

標記-整理

標記複製法算是完美的補齊了標記清除法的短板,即解決了空間碎片的問題,又適合使用在大部分物件都是可回收的場景。 不過標記複製法也有不完美的地方,一方面是需要空閒出一塊記憶體空間用來騰挪物件,另外一方面它在存活物件比較多的場景也不是太適合,而存活物件多的場景通常適合使用標記清除法,但是標記清除法會產生空間碎片又是一個無法忍受的問題

所以就需要有一種演算法,專門針對存活物件多,但是又產生空間碎片,還不浪費記憶體空間,這就是標記整理法的初衷

標記整理法分為標記和整理兩個階段,標記階段會先把存活的物件和可回收的物件標記出來;標記完再對記憶體物件進行整理,這個階段會把存活的物件往記憶體的一端移動,移動完物件後再清除存活物件邊界之外的物件

優點:

標記整理法是解決了標記複製法浪費空間、不適合存活物件多場景的短板,又解決了標記清除法空間碎片的短板, 所以對於標記複製法不適合的場景,同時又不能忍受標記清除法的空間碎片問題,就可以考慮標記整理法

缺點:

沒有任何一種演算法是萬能的,標記整理法看似解決了很多問題,但它本身存在很嚴重的效能問題,標記整理法是三種垃圾回收演算法中效能最低的一種,因為標記整理法在移動物件的時候不僅需要移動物件,還要額外的維護物件的引用的地址,這個過程可能要對記憶體經過幾次的掃描定位才能完成,同時還有清除物件的空座,既然做的事情這麼多那麼必然消耗的時間也越多

適用場景:記憶體吃緊,又要避免空間碎片的場景,老年代想要避免空間碎片問題的話通常會使用標記整理法