什么是雙親委派模型?
如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把這個請求任務委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務,就返回成功;只有父類加載器無法完成此加載任務時,才由下一級去加載
JVM為什么采用雙親委派機制
-
通過雙親委派機制可以避免某一個類被重復加載,當父類已經加載后則無需重復加載,保證唯一性
-
為了安全,保證類庫
API
不會被修改
JVM由哪些部分組成,運行流程是什么?
JVM
中有四大部分,分別是類加載器、運行時數據區,內存分區、執行引擎、本地庫接口
運行流程是:
第一,類加載器把 Java
代碼轉換為字節碼
第二,運行時數據區把字節碼加載到內存中
第三,執行引擎將字節碼翻譯為底層系統指令,交給CPU執行
介紹一下程序計數器的作用?
記錄當前線程正在執行的字節碼指令的地址(行號)
說一下堆棧的區別?
第一,棧內存一般會用來存儲局部變量和方法調用,但堆內存是用來存儲Java對象和數組的的。堆會GC垃圾回收,而棧不會
第二、棧內存是線程私有的,而堆內存是線程共有的
第三、兩者異常錯誤不同,但如果棧內存或者堆內存不足都會拋出異常
棧空間不足:java.lang.StackOverFlowError
堆空間不足:java.lang.OutOfMemoryError
什么是類加載器,類加載器有哪些?
類加載器(ClassLoader
)的作用就是將字節碼文件加載到 JVM
中,從而啟動Java
程序
常見的類加載器:
-
啟動類加載器(
BootStrap ClassLoader
):用于加載JAVA_HOME/jre/lib
目錄下的類庫 -
擴展類加載器(
ExtClassLoader
):加載JAVA_HOME/jre/lib/ext
目錄中的類庫 -
應用類加載器(
AppClassLoader
):用于加載classPath
下的類,也就是開發者自己編寫的Java
類 -
自定義類加載器:開發者自定義類繼承
ClassLoader
,實現自定義類加載規則
說一下類裝載的執行過程?
整個生命周期包括:加載、驗證、準備、解析、初始化、使用和卸載7個階段。其中,驗證、準備和解析這三個部分統稱為連接
1.加載:查找和導入 class
文件
2.驗證:保證加載類的準確性
3.準備:為類變量分配內存并設置類變量初始值
4.解析:把類中的符號引用轉換為直接引用
5.初始化:初始化類的靜態變量,靜態代碼塊
6.使用:JVM
從入口方法開始執行用戶的程序代碼
7.卸載:當用戶程序代碼執行完后,JVM 開始銷毀創建的 Class
對象,最后 JVM
也退出內存
簡述Java垃圾回收機制?(GC是什么?為什么要GC)
-
為了讓程序員更專注于代碼的實現,而不用考慮內存釋放的問題,所以有了自動的垃圾回收機制,也就是
GC
-
有了垃圾回收機制后,程序員只需要關心內存的申請即可,不用關心內存的釋放
強引用、軟引用、弱引用、虛引用的區別?
-
強引用表示一個對象處于有用且必須的狀態,如果一個對象具有強引用,則
GC
不會回收它。即便堆中內存不足,出現OOM
(OutOfMemoryError,內存資源耗盡),也不會對其進行回收 -
軟引用表示一個對象處于有用且非必須狀態,在內存充足時保留對象,而在內存不足時允許垃圾回收器(GC)自動回收這些對象??
-
弱引用表示一個對象處于可能有用且非必須的狀態。無論內存是否充足,??GC運行時發現該對象都會將其回收??
-
虛引用表示一個對象處于無用的狀態。在任何時候都有可能被垃圾回收
對象什么時候可以被垃圾器回收
如果一個或多個對象沒有任何的引用指向它了,那么這個對象有可能會被回收
如果要定位什么是垃圾,有兩種方式,一是引用計數法,二是可達性分析算法
通常使用可達性分析算法
來確定是不是垃圾
說一下 JVM 有哪些垃圾回收器?
在 jvm
中有多種垃圾收集器,包括:串行垃圾收集器、并行垃圾收集器(JDK8默認)、CMS(并發)垃圾收集器、G1垃圾收集器(JDK9默認)
JVM 垃圾回收算法有哪些?
一共有四種,分別是標記清除算法、復制算法、標記整理算法、分代回收
假如項目中產生了java內存泄露,說一下你的排查思路?
首先可以通過 jmap
指定打印他的內存快照 dump
文件,不過有的時候打印不了,可以設置 jvm
參數讓程序自動生成 dump
文件
然后通過工具去分析 dump
文件,jdk
自帶的 VisualVM
就可以分析
然后查看堆信息的情況,可以大概定位內存溢出是哪行代碼出了問題
找到對應的代碼,通過閱讀上下文的情況,進行修復
服務器CPU持續飆高,你的排查方案與思路?
第一可以使用使用 top
命令查看占用 cpu
的情況
然后查看是哪一個進程占用 cpu
較高,記錄這個進程 id
第三通過 ps
查看當前進程中的線程信息,看看哪個線程的 cpu
占用較高
再通過 jstack
命令打印進行的 id
,找到這個線程,就可以進一步定位問題代碼的行號
JVM 調優的參數可以在哪里設置參數值?
springboot項目在項目啟動的時候,java -jar
中加入參數就行了
面試官:用的 JVM 調優的參數都有哪些?
候選人:
嗯,這些參數是比較多的
我記得當時我們設置過堆的大小,像-Xms和-Xmx
還有就是可以設置年輕代中Eden區和兩個Survivor區的大小比例
還有就是可以設置使用哪種垃圾回收器等等。具體的指令還真記不太清楚。
調試 JVM都用了哪些工具?
jdk
自帶的一些工具,比如:
jps
輸出JVM中運行的進程狀態信息
jstack
查看 java
進程內線程的堆棧信息
jmap
用于生成堆轉存快照
jstat
用于 JVM
統計監測工具
還有一些可視化工具,像 jconsole
和 VisualVM
等
詳細說一下 JVM 運行時數據區?
運行時數據區包含堆、方法區、棧、本地方法棧、程序計數器
-
堆解決的是對象實例存儲的問題
-
方法區可以認為是堆的一部分,用于存儲已被虛擬機加載的信息,常量、靜態變量、即時編譯器編譯后的代碼
-
棧解決的是程序運行的問題,棧里面存的是棧幀,棧幀里面存的是局部變量表、操作數棧、動態鏈接、方法出口等信息
-
本地方法棧與棧功能相同,本地方法棧執行的是本地方法,是
Java
調用非Java
代碼的接口 -
程序計數器(PC寄存器)程序計數器中存放的是當前線程所執行的字節碼的行數。
JVM
工作時就是通過改變這個計數器的值來選取下一個需要執行的字節碼指令
你能詳細聊一下分代回收嗎?
關于分代回收是這樣的
在java8時,堆被分為了兩份:新生代和老年代,它們默認空間占用比例是1:2
對于新生代,內部又被分為了三個區域。Eden區,S0區,S1區默認空間占用比例是8:1:1
具體的工作機制是:
1)當創建一個對象的時候,那么這個對象會被分配在新生代的Eden區。當Eden區要滿了時候,觸發YoungGC。
2)當進行YoungGC后,此時在Eden區存活的對象被移動到S0區,并且當前對象的年齡會加1,清空Eden區。
3)當再一次觸發YoungGC的時候,會把Eden區中存活下來的對象和S0中的對象,移動到S1區中,這些對象的年齡會加1,清空Eden區和S0區。
4)當再一次觸發YoungGC的時候,會把Eden區中存活下來的對象和S1中的對象,移動到S0區中,這些對象的年齡會加1,清空Eden區和S1區。
5)對象的年齡達到了某一個限定的值(默認15歲 ),那么這個對象就會進入到老年代中。
當然也有特殊情況,如果進入Eden區的是一個大對象,在觸發YoungGC的時候,會直接存放到老年代
當老年代滿了之后,觸發FullGC。FullGC同時回收新生代和老年代,當前只會存在一個FullGC的線程進行執行,其他的線程全部會被掛起。 我們在程序中要盡量避免FullGC的出現。
講一下新生代、老年代、永久代的區別?
新生代主要用來存放新生的對象。
老年代主要存放應用中生命周期長的內存對象。
永久代指的是永久保存區域。主要存放Class和Meta(元數據)的信息。在Java8中,永久代已經被移除,取而代之的是一個稱之為“元數據區”(元空間)的區域。元空間和永久代類似,不過元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地內存。因此,默認情況下,元空間的大小僅受本地內存的限制。