變禿了,也變強了!爆肝吐血整理出的超硬核JVM筆記分享!
寫在前面
最近,一直有小夥伴讓我整理下關於JVM的知識,經過十幾天的收集與整理,初版算是整理出來了。希望對大家有所幫助。
記得點贊收藏加關注哦 ,需要下載PDF版本和更多知識點、面試題的朋友可以點一點下方連結免費領取
連結:點這裡!!! 799215493 暗號:CSDN
JDK 是什麼?
JDK 是用於支援 Java 程式開發的最小環境。
- Java 程式設計語言
- Java 虛擬機器
- Java API類庫
JRE 是什麼?
JRE 是支援 Java 程式執行的標準環境。
- Java SE API 子集
- Java 虛擬機器
Java歷史版本的特性?
Java Version SE 5.0
- 引入泛型;
- 增強迴圈,可以使用迭代方式;
- 自動裝箱與自動拆箱;
- 型別安全的列舉;
- 可變引數;
- 靜態引入;
- 元資料(註解);
- 引入Instrumentation。
Java Version SE 6
- 支援指令碼語言;
- 引入JDBC 4.0 API;
- 引入Java Compiler API;
- 可插拔註解;
- 增加對Native PKI(Public Key Infrastructure)、Java GSS(Generic Security Service)、Kerberos和LDAP(Lightweight Directory Access Protocol)的支援;
- 繼承Web Services;
- 做了很多優化。
Java Version SE 7
- switch語句塊中允許以字串作為分支條件;
- 在建立泛型物件時應用型別推斷;
- 在一個語句塊中捕獲多種異常;
- 支援動態語言;
- 支援try-with-resources;
- 引入Java NIO.2開發包;
- 數值型別可以用2進位制字串表示,並且可以在字串表示中新增下劃線;
- 鑽石型語法;
- null值的自動處理。
Java 8
- 函式式介面
- Lambda表示式
- Stream API
- 介面的增強
- 時間日期增強API
- 重複註解與型別註解
- 預設方法與靜態方法
- Optional 容器類
執行時資料區域包括哪些?
- 程式計數器
- Java 虛擬機器棧
- 本地方法棧
- Java 堆
- 方法區
- 執行時常量池
- 直接記憶體
程式計數器(執行緒私有)
程式計數器(Program Counter Register)是一塊較小的記憶體空間,可以看作是當前執行緒所執行位元組碼的行號指示器。分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器完成。
由於 Java 虛擬機器的多執行緒是通過執行緒輪流切換並分配處理器執行時間的方式實現的。為了執行緒切換後能恢復到正確的執行位置,每條執行緒都需要一個獨立的程式計數器,各執行緒之間的計數器互不影響,獨立儲存。
- 如果執行緒正在執行的是一個 Java 方法,計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;
- 如果正在執行的是 Native 方法,這個計數器的值為空。
程式計數器是唯一一個沒有規定任何 OutOfMemoryError 的區域。
Java 虛擬機器棧(執行緒私有)
Java 虛擬機器棧(Java Virtual Machine Stacks)是執行緒私有的,生命週期與執行緒相同。
虛擬機器棧描述的是 Java 方法執行的記憶體模型:每個方法被執行的時候都會建立一個棧幀(Stack Frame),儲存
- 區域性變量表
- 操作棧
- 動態連結
- 方法出口
每一個方法被呼叫到執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。
這個區域有兩種異常情況:
- StackOverflowError:執行緒請求的棧深度大於虛擬機器所允許的深度
- OutOfMemoryError:虛擬機器棧擴充套件到無法申請足夠的記憶體時
本地方法棧(執行緒私有)
虛擬機器棧為虛擬機器執行 Java 方法(位元組碼)服務。
本地方法棧(Native Method Stacks)為虛擬機器使用到的 Native 方法服務。
Java 堆(執行緒共享)
Java 堆(Java Heap)是 Java 虛擬機器中記憶體最大的一塊。Java 堆在虛擬機器啟動時建立,被所有執行緒共享。
作用:存放物件例項。垃圾收集器主要管理的就是 Java 堆。Java 堆在物理上可以不連續,只要邏輯上連續即可。
方法區(執行緒共享)
方法區(Method Area)被所有執行緒共享,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
和 Java 堆一樣,不需要連續的記憶體,可以選擇固定的大小,更可以選擇不實現垃圾收集。
執行時常量池
執行時常量池(Runtime Constant Pool)是方法區的一部分。儲存 Class 檔案中的符號引用、翻譯出來的直接引用。執行時常量池可以在執行期間將新的常量放入池中。
如何判斷物件是否“死去”?
- 引用計數法
- 根搜尋演算法
什麼是引用計數法?
給物件新增一個引用計數器,每當有一個地方引用它,計數器就+1,;當引用失效時,計數器就-1;任何時刻計數器都為0的物件就是不能再被使用的。
引用計數法的缺點?
很難解決物件之間的迴圈引用問題。
Java 的4種引用方式?
在 JDK 1.2 之後,Java 對引用的概念進行了擴充,將引用分為
- 強引用 Strong Reference
- 軟引用 Soft Reference
- 弱引用 Weak Reference
- 虛引用 Phantom Reference
強引用
Object obj = new Object();
程式碼中普遍存在的,像上述的引用。只要強引用還在,垃圾收集器永遠不會回收掉被引用的物件。
軟引用
用來描述一些還有用,但並非必須的物件。軟引用所關聯的物件,有在系統將要發生記憶體溢位異常之前,將會把這些物件列進回收範圍,並進行第二次回收。如果這次回收還是沒有足夠的記憶體,才會丟擲記憶體異常。提供了 SoftReference 類實現軟引用。
弱引用
描述非必須的物件,強度比軟引用更弱一些,被弱引用關聯的物件,只能生存到下一次垃圾收集發生前。當垃圾收集器工作時,無論當前記憶體是否足夠,都會回收掉只被弱引用關聯的物件。提供了 WeakReference 類來實現弱引用。
虛引用
一個物件是否有虛引用,完全不會對其生存時間夠成影響,也無法通過虛引用來取得一個物件例項。為一個物件關聯虛引用的唯一目的,就是希望在這個物件被收集器回收時,收到一個系統通知。提供了 PhantomReference 類來實現虛引用。
有哪些垃圾收集演算法?
- 標記-清除演算法
- 複製演算法
- 標記-整理演算法
- 分代收集演算法
分代收集演算法
根據物件的存活週期,將記憶體劃分為幾塊。一般是把 Java 堆分為新生代和老年代,這樣就可以根據各個年代的特點,採用最適當的收集演算法。
- 新生代:每次垃圾收集時會有大批物件死去,只有少量存活,所以選擇複製演算法,只需要少量存活物件的複製成本就可以完成收集。
- 老年代:物件存活率高、沒有額外空間對它進行分配擔保,必須使用“標記-清理”或“標記-整理”演算法進行回收。
記得點贊收藏加關注哦 ,需要下載PDF版本和更多知識點、面試題的朋友可以點一點下方連結免費領取
連結:點這裡!!! 799215493 暗號:CSDN
Minor GC 和 Full GC有什麼區別?
Minor GC:新生代 GC,指發生在新生代的垃圾收集動作,因為 Java 物件大多死亡頻繁,所以 Minor GC 非常頻繁,一般回收速度較快。
Full GC:老年代 GC,也叫 Major GC,速度一般比 Minor GC 慢 10 倍以上。
Java 記憶體
為什麼要將堆記憶體分割槽?
對於一個大型的系統,當建立的物件及方法變數比較多時,即堆記憶體中的物件比較多,如果逐一分析物件是否該回收,效率很低。分割槽是為了進行模組化管理,管理不同的物件及變數,以提高 JVM 的執行效率。
堆記憶體分為哪幾塊?
- Young Generation Space 新生區(也稱新生代)
- Tenure Generation Space養老區(也稱舊生代)
- Permanent Space 永久儲存區
分代收集演算法
記憶體分配有哪些原則?
- 物件優先分配在 Eden
- 大物件直接進入老年代
- 長期存活的物件將進入老年代
- 動態物件年齡判定
- 空間分配擔保
Young Generation Space (採用複製演算法)
主要用來儲存新建立的物件,記憶體較小,垃圾回收頻繁。這個區又分為三個區域:一個 Eden Space 和兩個 Survivor Space。
- 當物件在堆建立時,將進入年輕代的Eden Space。
- 垃圾回收器進行垃圾回收時,掃描Eden Space和A Suvivor Space,如果物件仍然存活,則複製到B Suvivor Space,如果B Suvivor Space已經滿,則複製 Old Gen
- 掃描A Suvivor Space時,如果物件已經經過了幾次的掃描仍然存活,JVM認為其為一個Old物件,則將其移到Old Gen。
- 掃描完畢後,JVM將Eden Space和A Suvivor Space清空,然後交換A和B的角色(即下次垃圾回收時會掃描Eden Space和B Suvivor Space。
Tenure Generation Space(採用標記-整理演算法)
主要用來儲存長時間被引用的物件。它裡面存放的是經過幾次在 Young Generation Space 進行掃描判斷過仍存活的物件,記憶體較大,垃圾回收頻率較小。
Permanent Space
儲存不變的類定義、位元組碼和常量等。
類載入器
類載入器的作用是什麼?
類載入器實現類的載入動作,同時用於確定一個類。對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性。即使兩個類來源於同一個Class檔案,只要載入它們的類載入器不同,這兩個類就不相等。
類載入器有哪些?
- 啟動類載入器(Bootstrap ClassLoader):使用C++實現(僅限於HotSpot),是虛擬機器自身的一部分。負責將存放在\lib目錄中的類庫載入到虛擬機器中。其無法被Java程式直接引用。
- 擴充套件類載入器(Extention ClassLoader)由ExtClassLoader實現,負責載入\lib\ext目錄中的所有類庫,開發者可以直接使用。
- 應用程式類載入器(Application ClassLoader):由APPClassLoader實現。負責載入使用者類路徑(ClassPath)上所指定的類庫。
類載入機制
什麼是雙親委派模型?
雙親委派模型(Parents Delegation Model)要求除了頂層的啟動類載入器外,其餘載入器都應當有自己的父類載入器。類載入器之間的父子關係,通過組合關係複用。
工作過程:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器完成。每個層次的類載入器都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有到父載入器反饋自己無法完成這個載入請求(它的搜尋範圍沒有找到所需的類)時,子載入器才會嘗試自己去載入。
為什麼要使用雙親委派模型,組織類載入器之間的關係?
Java類隨著它的類載入器一起具備了一種帶優先順序的層次關係。比如java.lang.Object,它存放在rt.jar中,無論哪個類載入器要載入這個類,最終都是委派給啟動類載入器進行載入,因此Object類在程式的各個類載入器環境中,都是同一個類。
如果沒有使用雙親委派模型,讓各個類載入器自己去載入,那麼Java型別體系中最基礎的行為也得不到保障,應用程式會變得一片混亂。
什麼是類載入機制?
Class檔案描述的各種資訊,都需要載入到虛擬機器後才能執行。虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。
虛擬機器和物理機的區別是什麼?
這兩種機器都有程式碼執行的能力,但是:
- 物理機的執行引擎是直接建立在處理器、硬體、指令集和作業系統層面的。
- 虛擬機器的執行引擎是自己實現的,因此可以自行制定指令集和執行引擎的結構體系,並且能夠執行那些不被硬體直接支援的指令集格式。
Java 方法呼叫
什麼是方法呼叫?
方法呼叫唯一的任務是確定被呼叫方法的版本(呼叫哪個方法),暫時還不涉及方法內部的具體執行過程。
Java的方法呼叫,有什麼特殊之處?
Class檔案的編譯過程不包含傳統編譯的連線步驟,一切方法呼叫在Class檔案裡面儲存的都只是符號引用,而不是方法在實際執行時記憶體佈局中的入口地址。這使得Java有強大的動態擴充套件能力,但使Java方法的呼叫過程變得相對複雜,需要在類載入期間甚至到執行時才能確定目標方法的直接引用。
Java虛擬機器呼叫位元組碼指令有哪些?
- invokestatic:呼叫靜態方法
- invokespecial:呼叫例項構造器方法、私有方法和父類方法
- invokevirtual:呼叫所有的虛方法
- invokeinterface:呼叫介面方法
虛擬機器是如何執行方法裡面的位元組碼指令的?
解釋執行(通過直譯器執行)
編譯執行(通過即時編譯器產生原生代碼)
解釋執行
當主流的虛擬機器中都包含了即時編譯器後,Class檔案中的程式碼到底會被解釋執行還是編譯執行,只有虛擬機器自己才能準確判斷。
Javac編譯器完成了程式程式碼經過詞法分析、語法分析到抽象語法樹,再遍歷語法樹生成線性的位元組碼指令流的過程。因為這一動作是在Java虛擬機器之外進行的,而直譯器在虛擬機器的內部,所以Java程式的編譯是半獨立的實現。
最後
由於篇幅有限,這裡只展示一部分,需要完整版的朋友可以點一點下方連結免費領取~
在這裡也為大家整理了各個知識點模組整理文件(微服務、資料庫、mysql、jvm、Redis等都有)和更多大廠面試真題,有需要的朋友可以點一點下方連結免費領取
連結:點這裡!!! 799215493 暗號:CSDN