變禿了,也變強(qiáng)了!爆肝吐血整理出的超硬核JVM筆記分享!

寫在前面

最近,一直有小伙伴讓我整理下關(guān)于JVM的知識,經(jīng)過十幾天的收集與整理,初版算是整理出來了。希望對大家有所幫助。

記得點(diǎn)贊收藏加關(guān)注哦 ,需要下載PDF版本和更多知識點(diǎn)、面試題的朋友可以點(diǎn)一點(diǎn)下方鏈接免費(fèi)領(lǐng)取

點(diǎn)擊這里免費(fèi)領(lǐng)取!!!暗號:簡書

JDK 是什么?

JDK 是用于支持 Java 程序開發(fā)的最小環(huán)境。

  1. Java 程序設(shè)計(jì)語言
  2. Java 虛擬機(jī)
  3. Java API類庫

JRE 是什么?

JRE 是支持 Java 程序運(yùn)行的標(biāo)準(zhǔn)環(huán)境。

  1. Java SE API 子集
  2. 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

  • 支持腳本語言;
  • 引入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;
  • 做了很多優(yōu)化。

Java Version SE 7

  • switch語句塊中允許以字符串作為分支條件;
  • 在創(chuàng)建泛型對象時(shí)應(yīng)用類型推斷;
  • 在一個(gè)語句塊中捕獲多種異常;
  • 支持動(dòng)態(tài)語言;
  • 支持try-with-resources;
  • 引入Java NIO.2開發(fā)包;
  • 數(shù)值類型可以用2進(jìn)制字符串表示,并且可以在字符串表示中添加下劃線;
  • 鉆石型語法;
  • 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ū)域包括哪些?

  1. 程序計(jì)數(shù)器
  2. Java 虛擬機(jī)棧
  3. 本地方法棧
  4. Java 堆
  5. 方法區(qū)
  6. 運(yùn)行時(shí)常量池
  7. 直接內(nèi)存

程序計(jì)數(shù)器(線程私有)

程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,可以看作是當(dāng)前線程所執(zhí)行字節(jié)碼的行號指示器。分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器完成。

由于 Java 虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式實(shí)現(xiàn)的。為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,各線程之間的計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)。

  1. 如果線程正在執(zhí)行的是一個(gè) Java 方法,計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;
  2. 如果正在執(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ǔ)

  1. 局部變量表
  2. 操作棧
  3. 動(dòng)態(tài)鏈接
  4. 方法出口

每一個(gè)方法被調(diào)用到執(zhí)行完成的過程,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

這個(gè)區(qū)域有兩種異常情況:

  1. StackOverflowError:線程請求的棧深度大于虛擬機(jī)所允許的深度
  2. OutOfMemoryError:虛擬機(jī)棧擴(kuò)展到無法申請足夠的內(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)建,被所有線程共享。

作用:存放對象實(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 文件中的符號引用、翻譯出來的直接引用。運(yùn)行時(shí)常量池可以在運(yùn)行期間將新的常量放入池中。

如何判斷對象是否“死去”?

  1. 引用計(jì)數(shù)法
  2. 根搜索算法

什么是引用計(jì)數(shù)法?

給對象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器就+1,;當(dāng)引用失效時(shí),計(jì)數(shù)器就-1;任何時(shí)刻計(jì)數(shù)器都為0的對象就是不能再被使用的。

引用計(jì)數(shù)法的缺點(diǎn)?

很難解決對象之間的循環(huán)引用問題。

Java 的4種引用方式?

在 JDK 1.2 之后,Java 對引用的概念進(jìn)行了擴(kuò)充,將引用分為

  1. 強(qiáng)引用 Strong Reference
  2. 軟引用 Soft Reference
  3. 弱引用 Weak Reference
  4. 虛引用 Phantom Reference

強(qiáng)引用

Object obj =  new  Object();

代碼中普遍存在的,像上述的引用。只要強(qiáng)引用還在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對象。

軟引用

用來描述一些還有用,但并非必須的對象。軟引用所關(guān)聯(lián)的對象,有在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對象列進(jìn)回收范圍,并進(jìn)行第二次回收。如果這次回收還是沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存異常。提供了 SoftReference 類實(shí)現(xiàn)軟引用。

弱引用

描述非必須的對象,強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對象,只能生存到下一次垃圾收集發(fā)生前。當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對象。提供了 WeakReference 類來實(shí)現(xiàn)弱引用。

虛引用

一個(gè)對象是否有虛引用,完全不會(huì)對其生存時(shí)間夠成影響,也無法通過虛引用來取得一個(gè)對象實(shí)例。為一個(gè)對象關(guān)聯(lián)虛引用的唯一目的,就是希望在這個(gè)對象被收集器回收時(shí),收到一個(gè)系統(tǒng)通知。提供了 PhantomReference 類來實(shí)現(xiàn)虛引用。

有哪些垃圾收集算法?

  1. 標(biāo)記-清除算法
  2. 復(fù)制算法
  3. 標(biāo)記-整理算法
  4. 分代收集算法

分代收集算法

根據(jù)對象的存活周期,將內(nèi)存劃分為幾塊。一般是把 Java 堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn),采用最適當(dāng)?shù)氖占惴ā?/p>

  • 新生代:每次垃圾收集時(shí)會(huì)有大批對象死去,只有少量存活,所以選擇復(fù)制算法,只需要少量存活對象的復(fù)制成本就可以完成收集。
  • 老年代:對象存活率高、沒有額外空間對它進(jìn)行分配擔(dān)保,必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法進(jìn)行回收。

記得點(diǎn)贊收藏加關(guān)注哦 ,需要下載PDF版本和更多知識點(diǎn)、面試題的朋友可以點(diǎn)一點(diǎn)下方鏈接免費(fèi)領(lǐng)取

鏈接:點(diǎn)擊這里免費(fèi)領(lǐng)取!!!暗號:簡書

Minor GC 和 Full GC有什么區(qū)別?

Minor GC:新生代 GC,指發(fā)生在新生代的垃圾收集動(dòng)作,因?yàn)?Java 對象大多死亡頻繁,所以 Minor GC 非常頻繁,一般回收速度較快。

Full GC:老年代 GC,也叫 Major GC,速度一般比 Minor GC 慢 10 倍以上。

Java 內(nèi)存

為什么要將堆內(nèi)存分區(qū)?

對于一個(gè)大型的系統(tǒng),當(dāng)創(chuàng)建的對象及方法變量比較多時(shí),即堆內(nèi)存中的對象比較多,如果逐一分析對象是否該回收,效率很低。分區(qū)是為了進(jìn)行模塊化管理,管理不同的對象及變量,以提高 JVM 的執(zhí)行效率。

堆內(nèi)存分為哪幾塊?

  1. Young Generation Space 新生區(qū)(也稱新生代)
  2. Tenure Generation Space養(yǎng)老區(qū)(也稱舊生代)
  3. Permanent Space 永久存儲(chǔ)區(qū)

分代收集算法

內(nèi)存分配有哪些原則?
  1. 對象優(yōu)先分配在 Eden
  2. 大對象直接進(jìn)入老年代
  3. 長期存活的對象將進(jìn)入老年代
  4. 動(dòng)態(tài)對象年齡判定
  5. 空間分配擔(dān)保
Young Generation Space (采用復(fù)制算法)

主要用來存儲(chǔ)新創(chuàng)建的對象,內(nèi)存較小,垃圾回收頻繁。這個(gè)區(qū)又分為三個(gè)區(qū)域:一個(gè) Eden Space 和兩個(gè) Survivor Space。

  • 當(dāng)對象在堆創(chuàng)建時(shí),將進(jìn)入年輕代的Eden Space。
  • 垃圾回收器進(jìn)行垃圾回收時(shí),掃描Eden Space和A Suvivor Space,如果對象仍然存活,則復(fù)制到B Suvivor Space,如果B Suvivor Space已經(jīng)滿,則復(fù)制 Old Gen
  • 掃描A Suvivor Space時(shí),如果對象已經(jīng)經(jīng)過了幾次的掃描仍然存活,JVM認(rèn)為其為一個(gè)Old對象,則將其移到Old Gen。
  • 掃描完畢后,JVM將Eden Space和A Suvivor Space清空,然后交換A和B的角色(即下次垃圾回收時(shí)會(huì)掃描Eden Space和B Suvivor Space。

Tenure Generation Space(采用標(biāo)記-整理算法)

主要用來存儲(chǔ)長時(shí)間被引用的對象。它里面存放的是經(jīng)過幾次在 Young Generation Space 進(jìn)行掃描判斷過仍存活的對象,內(nèi)存較大,垃圾回收頻率較小。

Permanent Space

存儲(chǔ)不變的類定義、字節(jié)碼和常量等。

類加載器

類加載器的作用是什么?

類加載器實(shí)現(xiàn)類的加載動(dòng)作,同時(shí)用于確定一個(gè)類。對于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在Java虛擬機(jī)中的唯一性。即使兩個(gè)類來源于同一個(gè)Class文件,只要加載它們的類加載器不同,這兩個(gè)類就不相等。

類加載器有哪些?

  1. 啟動(dòng)類加載器(Bootstrap ClassLoader):使用C++實(shí)現(xiàn)(僅限于HotSpot),是虛擬機(jī)自身的一部分。負(fù)責(zé)將存放在\lib目錄中的類庫加載到虛擬機(jī)中。其無法被Java程序直接引用。
  2. 擴(kuò)展類加載器(Extention ClassLoader)由ExtClassLoader實(shí)現(xiàn),負(fù)責(zé)加載\lib\ext目錄中的所有類庫,開發(fā)者可以直接使用。
  3. 應(yīng)用程序類加載器(Application ClassLoader):由APPClassLoader實(shí)現(xiàn)。負(fù)責(zé)加載用戶類路徑(ClassPath)上所指定的類庫。

類加載機(jī)制

什么是雙親委派模型?

雙親委派模型(Parents Delegation Model)要求除了頂層的啟動(dòng)類加載器外,其余加載器都應(yīng)當(dāng)有自己的父類加載器。類加載器之間的父子關(guān)系,通過組合關(guān)系復(fù)用。

工作過程:如果一個(gè)類加載器收到了類加載的請求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請求委派給父類加載器完成。每個(gè)層次的類加載器都是如此,因此所有的加載請求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中,只有到父加載器反饋?zhàn)约簾o法完成這個(gè)加載請求(它的搜索范圍沒有找到所需的類)時(shí),子加載器才會(huì)嘗試自己去加載。

為什么要使用雙親委派模型,組織類加載器之間的關(guān)系?

Java類隨著它的類加載器一起具備了一種帶優(yōu)先級的層次關(guān)系。比如java.lang.Object,它存放在rt.jar中,無論哪個(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)存,并對數(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)行過程。

Java的方法調(diào)用,有什么特殊之處?

Class文件的編譯過程不包含傳統(tǒng)編譯的連接步驟,一切方法調(diào)用在Class文件里面存儲(chǔ)的都只是符號引用,而不是方法在實(shí)際運(yùn)行時(shí)內(nèi)存布局中的入口地址。這使得Java有強(qiáng)大的動(dòng)態(tài)擴(kuò)展能力,但使Java方法的調(diào)用過程變得相對復(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í)行(通過解釋器執(zhí)行)
編譯執(zhí)行(通過即時(shí)編譯器產(chǎn)生本地代碼)

解釋執(zhí)行

當(dāng)主流的虛擬機(jī)中都包含了即時(shí)編譯器后,Class文件中的代碼到底會(huì)被解釋執(zhí)行還是編譯執(zhí)行,只有虛擬機(jī)自己才能準(zhǔn)確判斷。

Javac編譯器完成了程序代碼經(jīng)過詞法分析、語法分析到抽象語法樹,再遍歷語法樹生成線性的字節(jié)碼指令流的過程。因?yàn)檫@一動(dòng)作是在Java虛擬機(jī)之外進(jìn)行的,而解釋器在虛擬機(jī)的內(nèi)部,所以Java程序的編譯是半獨(dú)立的實(shí)現(xiàn)。

最后

在這里也為大家整理了各個(gè)知識點(diǎn)模塊整理文檔(微服務(wù)、數(shù)據(jù)庫、mysql、jvm、Redis等都有)和更多大廠面試真題,有需要的朋友可以點(diǎn)一點(diǎn)下方鏈接免費(fèi)領(lǐng)取

點(diǎn)擊這里免費(fèi)領(lǐng)取!!!暗號:簡書


由于篇幅有限,這里只展示一部分,需要完整版的朋友可以點(diǎn)一點(diǎn)下方鏈接免費(fèi)領(lǐng)取~

點(diǎn)擊這里免費(fèi)領(lǐng)取!!!暗號:簡書

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容