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