ZeroClipboard實現多個瀏覽器相容的複製文字到剪貼簿的功能
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
效能不穩定:記憶體中需要回收的物件,當記憶體中大量物件都是需要回收的時候,通常這些物件可能比較分散,所以清除的過程會比較耗時,這個時候清理的速度就會比較慢了
使用場景:只有小部分物件需要進行回收的,所以標記清除法比較適用於老年代的垃圾回收,因為老年代一般存活物件會比回收物件要多
標記-複製
將記憶體分為大小相同的兩塊,每次使用其中的一塊。當這一塊記憶體使用完之後,就將存活物件複製到另一塊記憶體中;然後再把使用的記憶體一次清理掉,這樣就使每次記憶體回收都是對記憶體空間的一半進行回收
優點:
解決了標記清除演算法的空間碎片問題
缺點:
會浪費一部分空間:總是會有一塊空閒的記憶體區域是利用不到的,這也造成了資源的浪費
存活物件多會非常耗時:因為複製移動物件的過程是比較耗時的,不僅需要移動物件本身還需要修改使用了這些物件的引用地址,所以當存活物件多的場景會非常耗時
需要擔保機制:因為複製區總會有一塊空間的浪費,而為了減少浪費空間太多,所以我們會把複製區的空間分配控制在很小的區間,但是空間太小又會產生一個問題,就是在存活的物件比較多的時候,這時複製區的空間可能不夠容納這些物件,這時就需要借一些空間來保證容納這些物件,這種從其他地方借記憶體的方式我們稱它為擔保機制
適用場景:比較適合存活物件較少的場景,這也正是新生代物件的特點,所以一般新生代的垃圾回收器基本都會選擇標記複製法
標記-整理
標記複製法算是完美的補齊了標記清除法的短板,即解決了空間碎片的問題,又適合使用在大部分物件都是可回收的場景。 不過標記複製法也有不完美的地方,一方面是需要空閒出一塊記憶體空間用來騰挪物件,另外一方面它在存活物件比較多的場景也不是太適合,而存活物件多的場景通常適合使用標記清除法,但是標記清除法會產生空間碎片又是一個無法忍受的問題
所以就需要有一種演算法,專門針對存活物件多,但是又產生空間碎片,還不浪費記憶體空間,這就是標記整理法的初衷
標記整理法分為標記和整理兩個階段,標記階段會先把存活的物件和可回收的物件標記出來;標記完再對記憶體物件進行整理,這個階段會把存活的物件往記憶體的一端移動,移動完物件後再清除存活物件邊界之外的物件
優點:
標記整理法是解決了標記複製法浪費空間、不適合存活物件多場景的短板,又解決了標記清除法空間碎片的短板, 所以對於標記複製法不適合的場景,同時又不能忍受標記清除法的空間碎片問題,就可以考慮標記整理法
缺點:
沒有任何一種演算法是萬能的,標記整理法看似解決了很多問題,但它本身存在很嚴重的效能問題,標記整理法是三種垃圾回收演算法中效能最低的一種,因為標記整理法在移動物件的時候不僅需要移動物件,還要額外的維護物件的引用的地址,這個過程可能要對記憶體經過幾次的掃描定位才能完成,同時還有清除物件的空座,既然做的事情這麼多那麼必然消耗的時間也越多
適用場景:記憶體吃緊,又要避免空間碎片的場景,老年代想要避免空間碎片問題的話通常會使用標記整理法