JVM知識回顧
1. 簡述JDK,JRE和JVM之間的關(guān)系
-
JDK:
是用于支持Java程序開發(fā)的最小環(huán)境例隆,基本上Java程序設(shè)計語言埂软、Java虛擬機么介、Java API類庫這三部分
JDK=JRE+java開發(fā)工具包
作用:java程序的開發(fā)環(huán)境
-
JRE:
是支持Java程序運行的標準環(huán)境拧咳,Java API類庫中的Java SE API自己和Java虛擬機這兩部分
java運行環(huán)境=JVM+核心類庫
作用:java程序的運行環(huán)境
-
JVM:
- 保證 Java程序設(shè)計語言的安全特性和java語言的跨平臺性
- 一次編譯诞挨,到處運行(Write once,run anywhere)
簡言之底靠,使用 jdk 開發(fā)的java程序揽趾,交由 JRE 來運行,由 JVM 來保證跨平臺
2. 類加載機制的作用和過程
一句話解釋:類的加載指的是將.class文件中的二進制數(shù)讀入到內(nèi)存中苛骨,將其放在運行數(shù)據(jù)區(qū)的方法區(qū)內(nèi)篱瞎,然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)痒芝。
類的加載過程包括加載俐筋、驗證、準備严衬、解析澄者、初始化五個階段
1)加載
? 在加載階段,虛擬機主要做了三件事
- 通過一個類的全限定類名來獲取其定義的二進制字節(jié)流
- 將這個字節(jié)流代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)
- 在堆中生成一個代表這個類的Class對象请琳,作為方法區(qū)中這些數(shù)據(jù)的訪問入口
2)驗證
? 保證被加載類的準確性
- 文件格式的驗證:驗證.class文件字節(jié)流是否符合class文件的格式規(guī)范粱挡,并且能夠被當前版本的虛擬機處理。這里主要對魔數(shù)俄精、主版本號询筏、常量池等等的校驗
- 元數(shù)據(jù)驗證:主要對字節(jié)碼描述的信息進行語義分析,以保證其描述的信息符合java語言規(guī)范的要求竖慧。比如說驗證這個類是不是有父類嫌套,類中的字段方法是不是和父類有沖突
- 字節(jié)碼驗證:這是整個驗證過程中最復(fù)雜的階段逆屡,主要是通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的踱讨、符合邏輯的魏蔗。在元數(shù)據(jù)驗證階段對數(shù)據(jù)類型做出驗證后,這個階段主要對類的方法做出分析痹筛,保證類的方法在運行時不會做出危害虛擬機的事莺治。
- 符號引用驗證:他是驗證的最后一個階段,發(fā)生在虛擬機將符號引用轉(zhuǎn)化為直接引用的時候帚稠。主要是對類自身以外的信息進行校驗谣旁。目的是確保解析動作能夠完成。
3)準備
? 準備階段主要為類變量分配內(nèi)存并設(shè)置初始值
類變量(static)會分配內(nèi)存翁锡,但實例變量不會蔓挖,實例變量主要隨著對象的實例化一塊分配到j(luò)ava堆中
-
這里的初始值值得是數(shù)據(jù)類型默認值夕土,不是代碼中被顯示賦予的值馆衔。比如
public static int a = 1;
這里的a在準備階段過后值為0,而不是1怨绣。賦值為1的動作在初始化階段角溃。
4)解析
? 解析階段主要是虛擬機將常量池中的符號引用轉(zhuǎn)為直接引用的過程
- 符號引用:以一組符號來描述所引用的目標,可以是任何形式的字面量篮撑。classfile中的內(nèi)容减细,沒有實際含義
- 直接引用:直接引用是可以指向目標的指針、相對偏移量或者是一個能直接或間接定位到目標的句柄赢笨。和虛擬機實現(xiàn)的內(nèi)存有關(guān)未蝌,不同的虛擬機直接引用一般不同。在java進程中能夠代表真實含義的
5)初始化
? 主要為類的靜態(tài)變量賦予正確的初始值茧妒,有兩種方式
- 聲明類變量時指定初始值
- 使用靜態(tài)代碼塊為類變量指定初始值
3. 運行時數(shù)據(jù)區(qū)分為哪幾塊?說說你對每塊區(qū)域的理解(某些存儲的內(nèi)容,生命周期,作用等)
? 運行時數(shù)據(jù)區(qū)分為方法區(qū)萧吠、堆、虛擬機棧桐筏、本地方法棧纸型、程序計數(shù)器
- 程序計數(shù)器:線程私有,可以看成是當前線程所執(zhí)行的字節(jié)碼的行號指示器梅忌,用于選取下一條需要執(zhí)行的指令狰腌,也是唯一一個沒有OOM情況的區(qū)域,與線程共存亡
- Java虛擬機棧:線程私有牧氮,為JVM執(zhí)行Java方法服務(wù)琼腔,主要描述Java方法執(zhí)行的線程內(nèi)存模型,與線程共存亡
- 本地方法棧:線程私有踱葛,與虛擬機棧相似展姐,為JVM使用到的Native方法服務(wù)躁垛,Native方法多用C++寫
- Java堆:線程共享,主要用于存儲實例對象圾笨,Java中幾乎所有的對象實例都會存儲到這塊內(nèi)存中教馆,考慮到即時編譯和逃逸分析,有一部分的對象實例會分配到棧上
- 方法區(qū):線程共享擂达,主要用于存儲被JVM加載的類信息土铺、常量、靜態(tài)變量板鬓、即時編譯器編譯后的代碼緩存等數(shù)據(jù)
- 運行時常量池:線程共享悲敷,屬于方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號引用
4. 結(jié)合Eden,S0,S1和Old區(qū),描述一下一個對象創(chuàng)建的過程
? 對象的分配俭令,從理論上來講應(yīng)該都是在堆上分配(即時編譯和逃逸分析后德,會分配到棧上)。在正常分代的情況中抄腔,新生成的對象會分配在新生代中瓢湃,但是少數(shù)情況下(比如對象大小超過一定閾值)會直接分配到老年代。
? 大多數(shù)情況下赫蛇,對象在Eden區(qū)中分配绵患,當Eden區(qū)沒有足夠空間時,虛擬機會發(fā)起一次Minor GC悟耘,新生代采用的是復(fù)制算法落蝙,把存活的對象從Eden和S0復(fù)制到S1區(qū),后把對象年齡加一暂幼,并清空Eden和S0(新生代的對象大多為活躍的對象筏勒,會頻繁的創(chuàng)建和銷毀,所以新生代GC時應(yīng)該存活的對象很少旺嬉,所以采用復(fù)制算法是最合適的)管行。當對象達到一定年齡之后(默認15歲,可修改-XX:MaxTenuringThresold=需要的年齡數(shù)字)鹰服,對象會被移動到老年代病瞳,當然老年代如果空間不足,會發(fā)生Major GC悲酷,采用標記整理算法(老年代的對象大部分都是經(jīng)過一段Minor GC的套菜,所以相對比較穩(wěn)定,每次回收的對象相對比較少设易,所以采用標記整理算法最合適)。
? 另外戏溺,一個對象的創(chuàng)建過程旷祸,上述描述只是一個對象的創(chuàng)建過程中的一個步驟中的詳解耕拷,一個對象的創(chuàng)建,從虛擬機的角度來看托享,包括:遇到new指令—判斷引用能否在常量池定位骚烧;檢查引用能否被加載、解析闰围、初始化過—分配內(nèi)存(包括指針碰撞和空閑列表法赃绊,這一部分內(nèi)容是我上面的描述)—初始化內(nèi)存空間—設(shè)置對象頭—執(zhí)行init()方法碧查。
5. 怎樣確定一個對象為垃圾
-
引用計數(shù)法
? 引用計數(shù)算法就是在對象中添加了一個引用計數(shù)器,當有地方引用這個對象時校仑,引用計數(shù)器的值就加1忠售,當引用失效的時候,引用計數(shù)器的值就減1邢滑。當引用計數(shù)器的值為0時,jvm就開始回收這個對象愿汰。
簡單的來說困后,在JVM中的棧中,如果棧幀中指向了一個對象衬廷,那么堆中的引用計數(shù)器的值就會加1摇予,當棧幀這個指向null時,對象的引用計數(shù)器就減1吗跋。
? 這種方法雖然很簡單侧戴、高效,但是JVM一般不會選擇這個方法跌宛,因為這個方法會出現(xiàn)一個問題:當對象之間相互指向時酗宋,兩個對象的引用計數(shù)器的值都會加1,而由于兩個對象時相互指向疆拘,所以引用不會失效蜕猫,這樣JVM就無法回收。
可達性分析法(Reachability Analysis)
所有生成的對象都是一個稱為"GC Roots"的根的子樹哎迄。從GC Roots開始向下搜索回右,搜索所經(jīng)過的路徑稱為引用鏈(Reference Chain)隆圆,當一個對象到“GC Roots”沒有任何引用鏈可以到達時,就稱這個對象是不可達的(不可引用的)翔烁,也就是可以被GC回收了渺氧。
6. 常用的垃圾回收算法有哪些?有什么優(yōu)缺點
-
**標記-清除 **
? 分為兩個階段:標記和清除。根據(jù)根節(jié)點可達分析哪些對象為垃圾蹬屹,并對垃圾進行標記阶女,然后統(tǒng)一回收。這是最基礎(chǔ)的算法哩治,后續(xù)的收集算法都是基于這個算法擴展的秃踩。
不足:
掃描整個堆內(nèi)存,耗時业筏,效率低憔杨;
標記清除之后會產(chǎn)生大量碎片。
-
標記整理
? 分為兩個階段:標記和整理蒜胖。根據(jù)根節(jié)點可達分析哪些對象為垃圾消别,并對垃圾進行標記,將還在被使用的對象移動到一端台谢。然后在清理掉這個范圍之外的垃圾寻狂。
優(yōu)點:
- 避免了內(nèi)存碎片
缺點:
- 整理內(nèi)存耗時
-
復(fù)制
? 將內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中一塊朋沮,當這一塊內(nèi)存用完蛇券,將還在被使用的對象復(fù)制到另一塊上,再將已使用過的那一塊內(nèi)存空間全部清理掉樊拓。
優(yōu)點:
- 效率高纠亚,實現(xiàn)簡單
缺點:
- 內(nèi)存空間利用率只用50%
7. 簡述你對吞吐量和停頓時間的理解
? 在實踐活動中,我們通過最優(yōu)吞吐量和最短停頓時間來評價jvm系統(tǒng)的性能:
? 吞吐量越高算法越好
? 暫停時間越短算法越好
? 首先讓我們來明確垃圾收集(GC)中的兩個術(shù)語:吞吐量(throughput)和暫停時間(pause times)筋夏。 JVM在專門的線程(GC threads)中執(zhí)行GC蒂胞。只要GC線程是活動的,它們將與應(yīng)用程序線程(application threads)爭用當前可用CPU的時鐘周期条篷。簡單點來說骗随,吞吐量是指應(yīng)用程序線程用時占程序總用時的比例。例如赴叹,吞吐量99/100意味著100秒的程序執(zhí)行時間應(yīng)用程序線程運行了99秒鸿染,而在這一時間段內(nèi)GC線程只運行了1秒。
? 術(shù)語”暫停時間”是指一個時間段內(nèi)應(yīng)用程序線程讓與GC線程執(zhí)行而完全暫停稚瘾。例如牡昆,GC期間100毫秒的暫停時間意味著在這100毫秒期間內(nèi)沒有應(yīng)用程序線程是活動的。 如果說一個正在運行的應(yīng)用程序有100毫秒的“平均暫停時間”,那么就是說該應(yīng)用程序所有的暫停時間平均長度為100毫秒丢烘。同樣柱宦,100毫秒的“最大暫停時間”是指該應(yīng)用程序所有的暫停時間最大不超過100毫秒。
8. 常見的jdk命令和工具有什么?并簡述一下他們的作用
? 常見的jdk命令:
-
jps
jps是JVM Process Status Tool的簡稱播瞳,用于顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機進程掸刊。常用的命令有:
jps -l //用于輸出主類的全名,如果運行的是jar包赢乓,則輸出jar路徑忧侧;
jps -v //用于輸出虛擬機啟動時的JVM參數(shù)。
[qianfg@Centos7 ~]$ jps -l 6563 eurekaserver-0.0.1-SNAPSHOT.jar 6564 manage-0.0.1-SNAPSHOT.war 6565 mybank-0.0.1-SNAPSHOT.war 6567 overseas-0.0.1-SNAPSHOT.war 5639 sun.tools.jps.Jps 17898 org.apache.rocketmq.namesrv.NamesrvStartup 17900 org.apache.rocketmq.broker.BrokerStartup 22783 org.apache.catalina.startup.Bootstrap
-
jstat
jstat是JVM Statistics Monitoring Tool的簡稱牌芋,用于顯示本地或遠程虛擬機進程中的類加載蚓炬、內(nèi)存、垃圾回收躺屁、JIT編譯等運行時數(shù)據(jù)肯夏。
jstat -class pid //類裝載信息
[qianfg@Centos7 ~]$ jstat -class 6563 Loaded Bytes Unloaded Bytes Time 11234 19528.8 0 0.0 60.17
-
jinfo
jinfo是Configuration Info for Java的簡稱,用于顯示虛擬機各項參數(shù)犀暑。常用的命令有:
jinfo -flag pid //用于查看未被顯示指定的參數(shù)的默認值;
jinfo -syspros pid //用于輸出虛擬機進程的System.getProperties()的內(nèi)容驯击。
[qianfg@Centos7 ~]$ jinfo -flag InitialHeapSize 22783 -XX:InitialHeapSize=62914560
-
jmap
jmap是Memory Map for Java的簡稱,用于生成堆轉(zhuǎn)儲快照文件耐亏、查詢finalize執(zhí)行隊列徊都、Java堆和永久代的詳細信息,需要配合具體的選項參數(shù)使用广辰。
[qianfg@Centos7 ~]$ jmap -heap 22783 Attaching to process ID 22783, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.221-b11 using thread-local object allocation. Parallel GC with 2 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 994050048 (948.0MB) NewSize = 20971520 (20.0MB) MaxNewSize = 331350016 (316.0MB) OldSize = 41943040 (40.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 18350080 (17.5MB) used = 14596944 (13.920730590820312MB) free = 3753136 (3.5792694091796875MB) 79.54703194754464% used From Space: capacity = 1572864 (1.5MB) used = 131072 (0.125MB) free = 1441792 (1.375MB) 8.333333333333334% used To Space: capacity = 1572864 (1.5MB) used = 0 (0.0MB) free = 1572864 (1.5MB) 0.0% used PS Old Generation capacity = 29884416 (28.5MB) used = 19405328 (18.506362915039062MB) free = 10479088 (9.993637084960938MB) 64.93460671943531% used 16678 interned Strings occupying 1501192 bytes.
-
jstack
jstack是Stack Trace for Java的簡稱暇矫,用于顯示當前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧集合,可用于分析線程長時間卡頓的原因轨域。
[qianfg@Centos7 ~]$ jstack 22783 2020-03-12 02:48:06 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode): "Attach Listener" #51 daemon prio=9 os_prio=0 tid=0x00007ff7fc007000 nid=0x1bbe waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "http-nio-8080-exec-15" #50 daemon prio=5 os_prio=0 tid=0x00007ff808026000 nid=0x3c29 waiting on condition [0x00007ff7f01c1000] java.lang.Thread.State: WAITING (parking) ...此處多行省略 "VM Thread" os_prio=0 tid=0x00007ff824074000 nid=0x5903 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007ff82401f800 nid=0x5901 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007ff824021000 nid=0x5902 runnable "VM Periodic Task Thread" os_prio=0 tid=0x00007ff8240c5800 nid=0x590a waiting on condition JNI global references: 212