JVM
JVM是Java Virtual Machine(Java虛擬機)的縮寫蟹略,JVM是一種用于計算設備的規(guī)范,它是一個虛構出來的計算機遏佣,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的挖炬。Java虛擬機包括一套字節(jié)碼指令集、一組寄存器状婶、一個棧意敛、一個垃圾回收堆和一個存儲方法域。 JVM屏蔽了與具體操作系統(tǒng)平臺相關的信息膛虫,使Java程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行草姻。JVM在執(zhí)行字節(jié)碼時,實際上最終還是把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行稍刀。
Java語言的一個非常重要的特點就是與平臺的無關性撩独。而使用Java虛擬機是實現(xiàn)這一特點的關鍵。一般的高級語言如果要在不同的平臺上運行账月,至少需要編譯成不同的目標代碼综膀。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯局齿。Java語言使用Java虛擬機屏蔽了與具體平臺相關的信息剧劝,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行抓歼。Java虛擬機在執(zhí)行字節(jié)碼時讥此,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行拢锹。這就是Java的能夠“一次編譯,到處運行”的原因暂论。
分享詳細解析十八道Java面試時常問的JVM題面褐,希望對各位有所幫助,另外我針對當前互聯(lián)網(wǎng)面試總結(jié)了一套“金四銀五Java面試突擊題庫”給大家取胎,文末有領取方式(誠意滿滿)
1展哭、內(nèi)存模型以及分區(qū),需要詳細到每個區(qū)放什么闻蛀。
JVM 分為堆區(qū)和棧區(qū)匪傍,還有方法區(qū),初始化的對象放在堆里面引用放在棧里面觉痛,class 類信息常量池(static 常量和 static 變量)等放在方法區(qū)
- 方法區(qū):主要是存儲類信息役衡,常量池(static 常量和 static 變量),編譯后的代碼(字節(jié)碼)等數(shù)據(jù)
- 堆:初始化的對象薪棒,成員變量 (那種非 static 的變量)手蝎,所有的對象實例和數(shù)組都要在堆上分配
- 棧:棧的結(jié)構是棧幀組成的,調(diào)用一個方法就壓入一幀俐芯,幀上面存儲局部變量表棵介,操作數(shù)棧,方法出口等信息吧史,局部變量表存放的是 8 大基礎類型加上一個應用類型邮辽,所以還是一個指向地址的指針
- 本地方法棧:主要為 Native 方法服務
- 程序計數(shù)器:記錄當前線程執(zhí)行的行號
2、堆里面的分區(qū):Eden贸营,survival (from+ to)吨述,老年代,各自的特點钞脂。
堆里面分為新生代和老生代(java8 取消了永久代揣云,采用了 Metaspace),新生代包含 Eden+Survivor 區(qū)芳肌,survivor 區(qū)里面分為 from 和 to 區(qū)灵再,內(nèi)存回收時,如果用的是復制算法亿笤,從 from 復制到 to翎迁,當經(jīng)過一次或者多次 GC 之后,存活下來的對象會被移動到老年區(qū)净薛,當 JVM 內(nèi)存不夠用的時候汪榔,會觸發(fā) Full GC,清理 JVM 老年區(qū)當新生區(qū)滿了之后會觸發(fā) YGC,先把存活的對象放到其中一個 Survice區(qū),然后進行垃圾清理痴腌。
因為如果僅僅清理需要刪除的對象雌团,這樣會導致內(nèi)存碎片,因此一般會把 Eden 進行完全的清理士聪,然后整理內(nèi)存锦援。那么下次 GC 的時候,就會使用下一個 Survive剥悟,這樣循環(huán)使用灵寺。如果有特別大的對象,新生代放不下区岗,就會使用老年代的擔保略板,直接放到老年代里面。因為 JVM 認為慈缔,一般大對象的存活時間一般比較久遠叮称。
3、對象創(chuàng)建方法藐鹤,對對象的內(nèi)存分配瓤檐,對象的訪問定位。
new 一個對象
4娱节、GC 的兩種判定方法
- 引用計數(shù)法:指的是如果某個地方引用了這個對象就+1距帅,如果失效了就-1,當為 0 就會回收但是 JVM 沒有用這種方式括堤,因為無法判定相互循環(huán)引用(A 引用 B,B 引用 A)的情況
- 引用鏈法: 通過一種 GC ROOT 的對象(方法區(qū)中靜態(tài)變量引用的對象等-static 變量)來判斷,如果有一條鏈能夠到達 GC ROOT 就說明绍移,不能到達 GC ROOT 就說明可以回收
5悄窃、SafePoint 是什么
比如 GC 的時候必須要等到 Java 線程都進入到 safepoint 的時候 VMThread 才能開始執(zhí)行 GC,
- 循環(huán)的末尾 (防止大循環(huán)的時候一直不進入 safepoint蹂窖,而其他線程在等待它進入safepoint)
- 方法返回前
- 調(diào)用方法的 call 之后
- 拋出異常的位置
6轧抗、GC 的三種收集方法:標記清除、標記整理瞬测、復制算法的原理與特點横媚,分別用在什么地方,如果讓你優(yōu)化收集方法月趟,有什么思路灯蝴?
先標記,標記完畢之后再清除孝宗,效率不高穷躁,會產(chǎn)生碎片復制算法:分為 8:1 的 Eden 區(qū)和 survivor 區(qū),就是上面談到的 YGC
標記整理:標記完畢之后因妇,讓所有存活的對象向一端移動
7问潭、GC 收集器有哪些猿诸?CMS 收集器與 G1 收集器的特點。
- 并行收集器:串行收集器使用一個單獨的線程進行收集狡忙,GC 時服務有停頓時間
- 串行收集器:次要回收中使用多線程來執(zhí)行
CMS 收集器是基于“標記—清除”算法實現(xiàn)的梳虽,經(jīng)過多次標記才會被清除G1 從整體來看是基于“標記—整理”算法實現(xiàn)的收集器,從局部(兩個 Region 之間)上來看是基于“復制”算法實現(xiàn)的
8灾茁、Minor GC 與 Full GC 分別在什么時候發(fā)生窜觉?
新生代內(nèi)存不夠用時候發(fā)生 MGC 也叫 YGC,JVM 內(nèi)存不夠的時候發(fā)生 FGC
9删顶、幾種常用的內(nèi)存調(diào)試工具:jmap竖螃、jstack、jconsole逗余、jhat
jstack 可以看當前棧的情況特咆,jmap 查看內(nèi)存,jhat 進行 dump 堆的信息
mat(eclipse 的也要了解一下)
10录粱、類加載的幾個過程:
加載腻格、驗證、準備啥繁、解析菜职、初始化。然后是使用和卸載了
通過全限定名來加載生成 class 對象到內(nèi)存中旗闽,然后進行驗證這個 class 文件酬核,包括文件格式校驗、元數(shù)據(jù)驗證适室,字節(jié)碼校驗等嫡意。準備是對這個對象分配內(nèi)存。解析是將符號引用轉(zhuǎn)化為直接引用(指針引用)捣辆,初始化就是開始執(zhí)行構造器的代碼
11蔬螟、JVM 內(nèi)存每個區(qū)的作用是什么?
java 虛擬機主要分為以下一個區(qū):
方法區(qū):
- 有時候也成為永久代汽畴,在該區(qū)內(nèi)很少發(fā)生垃圾回收旧巾,但是并不代表不發(fā)生 GC,在這里進行的 GC 主要是對方法區(qū)里的常量池和對類型的卸載
- 方法區(qū)主要用來存儲已被虛擬機加載的類的信息忍些、常量鲁猩、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)。
- 該區(qū)域是被線程共享的罢坝。
- 方法區(qū)里有一個運行時常量池绳匀,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號引用。該常量池具有動態(tài)性,也就是說常量并不一定是編譯時確定疾棵,運行時生成的常量也會存在這個常量池中戈钢。
虛擬機棧:
- 虛擬機棧也就是我們平常所稱的棧內(nèi)存,它為 java 方法服務,每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀是尔,用于存儲局部變量表殉了、操作數(shù)棧、動態(tài)鏈接和方法出口等信息拟枚。
- 虛擬機棧是線程私有的薪铜,它的生命周期與線程相同。
- 局部變量表里存儲的是基本數(shù)據(jù)類型恩溅、returnAddress 類型(指向一條字節(jié)碼指令的地址)和對象引用隔箍,這個對象引用有可能是指向?qū)ο笃鹗嫉刂返囊粋€指針,也有可能是代表對象的句柄或者與對象相關聯(lián)的位置脚乡。局部變量所需的內(nèi)存空間在編譯器間確定
- 操作數(shù)棧的作用主要用來存儲運算結(jié)果以及運算的操作數(shù)蜒滩,它不同于局部變量表通過索引來訪問,而是壓棧和出棧的方式
- 每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用奶稠,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接.動態(tài)鏈接就是將常量池中的符號引用在運行期轉(zhuǎn)化為直接引用俯艰。
本地方法棧:
本地方法棧和虛擬機棧類似,只不過本地方法棧為 Native 方法服務锌订。
堆:
java 堆是所有線程所共享的一塊內(nèi)存竹握,在虛擬機啟動時創(chuàng)建,幾乎所有的對象實例都在這里創(chuàng)建辆飘,因此該區(qū)域經(jīng)常發(fā)生垃圾回收操作啦辐。
程序計數(shù)器:
內(nèi)存空間小,字節(jié)碼解釋器工作時通過改變這個計數(shù)值可以選取下一條需要執(zhí)行的字節(jié)碼指令蜈项,分支昧甘、循環(huán)、跳轉(zhuǎn)战得、異常處理和線程恢復等功能都需要依賴這個計數(shù)器完成。該內(nèi)存區(qū)域是唯一一個 java 虛擬機規(guī)范沒有規(guī)定任何 OOM 情況的區(qū)域庸推。
12常侦、如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
判斷一個對象是否存活有兩種方法:
1. 引用計數(shù)法
所謂引用計數(shù)法就是給每一個對象設置一個引用計數(shù)器,每當有一個地方引用這個對象時贬媒,就將計數(shù)器加一聋亡,引用失效時,計數(shù)器就減一际乘。當一個對象的引用計數(shù)器為零時坡倔,說明此對象沒有被引用,也就是“死對象”,將會被垃圾回收.引用計數(shù)法有一個缺陷就是無法解決循環(huán)引用問題,也就是說當對象 A 引用對象 B罪塔,對象B 又引用者對象 A投蝉,那么此時 A,B 對象的引用計數(shù)器都不為零,也就造成無法完成垃圾回收征堪,所以主流的虛擬機都沒有采用這種算法瘩缆。
2. 可達性算法(引用鏈法)
該算法的思想是:從一個被稱為 GC Roots 的對象開始向下搜索,如果一個對象到 GCRoots 沒有任何引用鏈相連時佃蚜,則說明此對象不可用庸娱。
在 java 中可以作為 GC Roots 的對象有以下幾種:
- 虛擬機棧中引用的對象
- 方法區(qū)類靜態(tài)屬性引用的對象
- 方法區(qū)常量池引用的對象
- 本地方法棧 JNI 引用的對象
雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時谐算,一個對象比不一定會被回收熟尉。當一個對象不可達 GC Root 時,這個對象并不會立馬被回收洲脂,而是出于一個死緩的階段斤儿,若要被真正的回收需要經(jīng)歷兩次標記如果對象在可達性分析中沒有與 GC Root 的引用鏈,那么此時就會被第一次標記并且進行一次篩選腮考,篩選的條件是是否有必要執(zhí)行 finalize()方法雇毫。當對象沒有覆蓋 finalize()方法或者已被虛擬機調(diào)用過,那么就認為是沒必要的踩蔚。
如果該對象有必要執(zhí)行 finalize()方法棚放,那么這個對象將會放在一個稱為 F-Queue 的對隊列中,虛擬機會觸發(fā)一個 Finalize()線程去執(zhí)行馅闽,此線程是低優(yōu)先級的飘蚯,并且虛擬機不會承諾一直等待它運行完,這是因為如果 finalize()執(zhí)行緩慢或者發(fā)生了死鎖福也,那么就會造成 F?Queue 隊列一直等待局骤,造成了內(nèi)存回收系統(tǒng)的崩潰。GC 對處于 F-Queue 中的對象進行第二次被標記暴凑,這時峦甩,該對象將被移除”即將回收”集合,等待回收现喳。
13凯傲、簡述 java 垃圾回收機制?
在 java 中嗦篱,程序員是不需要顯示的去釋放一個對象的內(nèi)存的冰单,而是由虛擬機自行執(zhí)行。在JVM 中灸促,有一個垃圾回收線程诫欠,它是低優(yōu)先級的涵卵,在正常情況下是不會執(zhí)行的,只有在虛擬機空閑或者當前堆內(nèi)存不足時荒叼,才會觸發(fā)執(zhí)行轿偎,掃面那些沒有被任何引用的對象,并將它們添加到要回收的集合中甩挫,進行回收贴硫。
14、java 中垃圾收集的方法有哪些伊者?
1.標記-清除:
這是垃圾收集算法中最基礎的英遭,根據(jù)名字就可以知道,它的思想就是標記哪些要被回收的對象亦渗,然后統(tǒng)一回收挖诸。這種方法很簡單,但是會有兩個主要問題:1.效率不高法精,標記和清除的效率都很低多律;2.會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導致以后程序在分配較大的對象時搂蜓,由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動作狼荞。
2.復制算法:
為了解決效率問題,復制算法將可用內(nèi)存按容量劃分為相等的兩部分帮碰,然后每次只使用其中的一塊相味,當一塊內(nèi)存用完時,就將還存活的對象復制到第二塊內(nèi)存上殉挽,然后一次性清楚完第一塊內(nèi)存丰涉,再將第二塊上的對象復制到第一塊。
但是這種方式斯碌,內(nèi)存的代價太高一死,每次基本上都要浪費一般的內(nèi)存。于是將該算法進行了改進傻唾,內(nèi)存區(qū)域不再是按照 1:1 去劃分投慈,而是將內(nèi)存劃分為8:1:1 三部分,較大那份內(nèi)存交 Eden 區(qū)冠骄,其余是兩塊較小的內(nèi)存區(qū)叫 Survior 區(qū)伪煤。每次都會優(yōu)先使用 Eden 區(qū),若 Eden 區(qū)滿猴抹,就將對象復制到第二塊內(nèi)存區(qū)上,然后清除 Eden 區(qū)锁荔,如果此時存活的對象太多蟀给,以至于 Survivor 不夠時蝙砌,會將這些對象通過分配擔保機制復制到老年代中。(java 堆又分為新生代和老年代)
3.標記-整理:
該算法主要是為了解決標記-清除跋理,產(chǎn)生大量內(nèi)存碎片的問題择克;當對象存活率較高時,也解決了復制算法的效率問題前普。它的不同之處就是在清除對象的時候現(xiàn)將可回收對象移動到一端肚邢,然后清除掉端邊界以外的對象,這樣就不會產(chǎn)生內(nèi)存碎片了拭卿。
4.分代收集:
現(xiàn)在的虛擬機垃圾收集大多采用這種方式骡湖,它根據(jù)對象的生存周期,將堆分為新生代和老年代峻厚。在新生代中响蕴,由于對象生存期短,每次回收都會有大量對象死去惠桃,那么這時就采用復制算法浦夷。老年代里的對象存活率較高,沒有額外的空間進行分配擔保辜王,所以可以使用標記-整理 或者 標記-清除劈狐。
15、簡述 java 類加載機制呐馆?
虛擬機把描述類的數(shù)據(jù)從 Class 文件加載到內(nèi)存肥缔,并對數(shù)據(jù)進行校驗,解析和初始化摹恰,最終形成可以被虛擬機直接使用的 java 類型辫继。
16、類加載器雙親委派模型機制俗慈?
當一個類收到了類加載請求時姑宽,不會自己先去加載這個類,而是將其委派給父類闺阱,由父類去加載炮车,如果此時父類不能加載,反饋給子類酣溃,由子類去完成類的加載瘦穆。
17、什么是類加載器赊豌,類加載器有哪些扛或?
實現(xiàn)通過類的權限定名獲取該類的二進制字節(jié)流的代碼塊叫做類加載器。
主要有一下四種類加載器:
- 啟動類加載器(Bootstrap ClassLoader)用來加載 java 核心類庫碘饼,無法被 java 程序直接引用熙兔。
- 擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫悲伶。Java 虛擬機的實現(xiàn)會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載 Java 類住涉。
- 系統(tǒng)類加載器(system class loader):它根據(jù) Java 應用的類路徑(CLASSPATH)來加載 Java 類麸锉。一般來說,Java 應用的類都是由它來完成加載的舆声』ǔ粒可以通過ClassLoader.getSystemClassLoader()來獲取它。
- 用戶自定義類加載器媳握,通過繼承 java.lang.ClassLoader 類的方式實現(xiàn)碱屁。
18、簡述 java 內(nèi)存分配與回收策率以及 Minor GC 和 Major GC
- 對象優(yōu)先在堆的 Eden 區(qū)分配毙芜。
- 大對象直接進入老年代.
- 長期存活的對象將直接進入老年代. 當 Eden 區(qū)沒有足夠的空間進行分配時忽媒,虛擬機會執(zhí)行一次 MinorGC.Minor Gc 通常發(fā)生在新生代的 Eden 區(qū),在這個區(qū)的對象生存期短腋粥,往往發(fā)生 Gc 的頻率較高晦雨,回收速度比較快;Full Gc/Major GC 發(fā)生在老年代,一般情況下隘冲,觸發(fā)老年代 GC的時候不會觸發(fā) Minor GC,但是通過配置闹瞧,可以在 Full GC 之前進行一次 MinorGC 這樣可以加快老年代的回收速度。
讀者福利
針對于上面的文章我總結(jié)出了互聯(lián)網(wǎng)公司java程序員面試涉及到的絕大部分面試題及答案做成了文檔和架構視頻資料免費分享給大家(包括Dubbo展辞、Redis奥邮、Netty、zookeeper罗珍、Spring cloud洽腺、分布式、高并發(fā)等架構技術資料)覆旱,希望能幫助到您面試前的復習且找到一個好的工作蘸朋,也節(jié)省大家在網(wǎng)上搜索資料的時間來學習。
資料獲取方式:
面試文件獲取方式:
進我的學習交流群:909666042 免費領取資料?鄢(助你面試無憂)
合理利用自己每一分每一秒的時間來學習提升自己藕坯,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕噪沙,使勁拼炼彪,給未來的自己一個交代!