每日六道java新手入門面試題,通往自由的道路--JVM
1. JVM是如何判斷對象是否可回收
垃圾收集器在做垃圾回收的時候,首先需要判斷一個對象是存活狀態(tài)還是死亡狀態(tài),死亡的對象將會被標(biāo)識為垃圾數(shù)據(jù)并等待收集器進行清除。
而判斷一個對象是否為可回收狀態(tài)的常用算法有兩個:引用計數(shù)器法和可達性分析算法。
- 引用計數(shù)器法:
在 Java 中,引用和對象是有關(guān)聯(lián)的,通過引用計數(shù)來判斷一個對象是否可以回收。它在創(chuàng)建對象時關(guān)聯(lián)一個與之相對應(yīng)的計數(shù)器,當(dāng)此對象被使用時加 1,相反銷毀時 -1。當(dāng)此計數(shù)器為 0 時,則表示此對象未使用,可以被垃圾收集器回收。其優(yōu)點是垃圾回收比較及時,實時性比較高,只要對象計數(shù)器為 0,則可以直接進行回收操作;而缺點是無法解決循環(huán)引用的問題。
- 可達性分析算法:
主要是從GC Root的對象為起點出發(fā),然后開始向下搜索,搜索走過的路徑稱為引用鏈,當(dāng)一個對象到GC Root之間沒有任何引用鏈的時候,代表這個對象不可以用,判斷為垃圾,就會被GC回收。
在 java 中可以作為 GC Roots 的對象有以下幾種:
- 虛擬機棧中引用的對象
- 方法區(qū)類靜態(tài)屬性引用的對象
- 方法區(qū)常量池引用的對象
- 本地方法棧 JNI 引用的對象
2. 你知道有什么垃圾回收的常見算法嗎?
標(biāo)記清除法:
分為標(biāo)記–清除兩個階段,首先先標(biāo)記出所有需要回收的對象,然后在標(biāo)記完成后統(tǒng)一清除回收所有被標(biāo)記的對象。
它可能產(chǎn)生的問題呢標(biāo)記清除后,產(chǎn)生一些大量不連續(xù)的內(nèi)存碎片,導(dǎo)致可能以后后續(xù)的大內(nèi)存找不到足夠的連續(xù)內(nèi)存再導(dǎo)致提前又發(fā)生一次垃圾收集
標(biāo)記整理法:
基本步驟和標(biāo)記清除類似,但是多了一步整理的步驟,讓所有村后的對象都向一端移動,然后清除掉不需要的內(nèi)存。
它解決了內(nèi)存碎片的問題,但是需要頻繁的移動存活的對象,效率就比較低了。
復(fù)制算法:
將可用的內(nèi)存分為一半,每次只使用一個區(qū)域。將需要存活的對象復(fù)制到另一個對象中去。
這種方法也是可以解決了內(nèi)存碎片問題,但是內(nèi)存對半分了,而且對象存活率高的對象需要頻繁復(fù)制。
基于前面的算法的話,JVM采用一個分代算法的形式:
對于JVM的堆來說分為了新生代和老年代兩個區(qū)域,而新生代也還分了eden區(qū)和兩個幸存區(qū),他們比例是8:1:1,
對于新生代來說采用了復(fù)制算法,因為對于新生代來說每次垃圾回收的存活的對象是比較少的,所以采用復(fù)制算法較好,而老年代的話,則采用了標(biāo)記整理法。
- 首先對象會先分配在eden區(qū),
- 然后再新生代空間不足時,會發(fā)生一次minor gc算法將存活的對象復(fù)制到幸存區(qū)s1中,并使存活的對象的年齡加1,然后s1和s0交換,。
- 在對象壽命超過閾值最大15時,就會晉升至老年代。
- 而當(dāng)老年代的空間不足時,會先嘗試觸發(fā)一次minor gc,如果空間還是不足的話。就會出發(fā)full gc ,而此時的stw會更長。
3. 你知道有什么垃圾收集器嗎?
常見的垃圾收集器有:其中用于回收新生代的收集器有Serial、PraNew、Parallel Scavenge,而回收老年代的收集器有Serial Old、Parallel Old、CMS,最后還有一個可以用于回收整個Java堆的G1收集器。
作用于新生代的:
- Serial 收集器屬于最早期的垃圾收集器,也是 JDK 1.3 版本之前唯一的垃圾收集器。它是單線程的垃圾收集器,采用復(fù)制算法,其意味著單線程是指在進行垃圾回收時所有的工作線程必須暫停,直到垃圾回收結(jié)束為止。
特點是簡單和高效,并且本身的運行對內(nèi)存要求不高,因此它在客戶端模式下使用的比較多。
- sParNew 收集器實際上是 Serial 收集器的多線程并行版本,也是采用復(fù)制算法。
- Parallel Scavenge 收集器和 ParNew 收集器類似,它也是一個并行運行的垃圾回收器;不同的點在于該收集器關(guān)注的側(cè)重點是實現(xiàn)一個可以控制的吞吐量。它的計算公式是:用戶運行代碼的時間 / (用戶運行代碼的時間 + 垃圾收集的時間)。比如用戶運行的時間是 8 分鐘,垃圾回收運行的時間是 2 分鐘,那么吞吐量就是 80%。Parallel Scavenge 收集器追目標(biāo)就是達到一個可控制的吞吐量,高吞吐量可以最高效率地利用CPU時候,盡快地完成程序的運算任務(wù)。
作用于老年代的:
- Serial Old 收集器為 Serial 收集器的老年代版本,而 Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本。兩者都是在老年代中采用這標(biāo)記—— 整理算法。
- CMS(Concurrent Mark Sweep)以獲取最短回收停頓時間為目標(biāo),與Parallel Scavenge 收集器不同,是基于標(biāo)記 —— 清除算法實現(xiàn)。它強調(diào)的是提供最短的停頓時間,因此可能會犧牲一定的吞吐量。它主要應(yīng)用在 Java Web 項目中,它滿足了系統(tǒng)需要短時間停頓的要求,以此來提高用戶的交互體驗。CMS 工作機制相比其他的垃圾收集器來說更復(fù)雜。整個過程分為以下 4 個階段:
- 初始標(biāo)記(CMS initial mark):標(biāo)記 GC Roots 能直接關(guān)聯(lián)到的對象
- 并發(fā)標(biāo)記(CMS concurrent mark):進行 GC Roots Tracing
- 重新標(biāo)記(CMS remark):修正并發(fā)標(biāo)記期間的變動部分并發(fā)清除
- (CMS concurrent sweep):清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。
作用于整個Java堆包括新生代和老年代的。
- Garbage First(簡稱 G1)收集器是歷史發(fā)展的產(chǎn)物,也是一款更先進的垃圾收集器,主要面向服務(wù)端應(yīng)用的垃圾收集器,是基于基于標(biāo)記-整理算法實現(xiàn),不產(chǎn)生內(nèi)存碎片。它將內(nèi)存劃分為多個 Region 分區(qū),回收時則以分區(qū)為單位進行回收,這樣它就可以用相對較少的時間優(yōu)先回收包含垃圾最多區(qū)塊。此外,G1收集器不同于之前的收集器的一個重要特點是:G1回收的范圍是整個Java堆(包括新生代,老年代),而前六種收集器回收的范圍僅限于新生代或老年代。從 JDK 9 之后也成了官方默認的垃圾收集器,官方也推薦使用 G1 來代替選擇 CMS 收集器。
4. 那你知道什么時候才會觸發(fā)Full GC
1.在老年代空間不足的時候:
老年代空間只有在新生代對象發(fā)生minor Gc轉(zhuǎn)入或者是直接創(chuàng)建為大對象、大數(shù)組時出現(xiàn)空間不足的現(xiàn)象,當(dāng)JVM執(zhí)行Full GC后空間仍然不足,則拋出如下錯誤:java.lang.OutOfMemoryError: Java heap space。
解決措施:盡量做到讓對象在Minor GC階段被回收、讓對象在新生代多存活一段時間及不要創(chuàng)建過大的對象及數(shù)組。
2.在我們程序中直接調(diào)用了System.gc, 也會直接出發(fā)Full GC。
3.在永久代空間滿
永久代中存放的為一些class的信息等,當(dāng)系統(tǒng)中要加載的類、反射的類和調(diào)用的方法較多時,永久代空間可能會被占滿,在未配置的時候采用這CMS垃圾收集器的情況下會執(zhí)行Full GC。如果經(jīng)過Full GC仍然回收不了,那么JVM會拋出如下錯誤信息:java.lang.OutOfMemoryError: PermGen space。
解決措施:可采用的方法為增大永久代空間或轉(zhuǎn)為使用CMS GC。
4.在CMS垃圾收集器出現(xiàn)promotion failed
(晉升失敗)和concurrent mode failure
(并發(fā)模式故障)
對于如果我們采用CMS垃圾收集器進行老年代GC的程序而言,我們就需要主要在GC日志中是否有晉升失敗和并發(fā)模式故障兩種狀況,當(dāng)這兩種狀況出現(xiàn)時可能會觸發(fā)Full GC:
晉升失?。?code>promotion failed) 是在新生代進行Minor GC時,幸存區(qū)中放不下、而對象只能放入老年代,而此時老年代也放不下造成的。
concurrent mode failure是CMS轉(zhuǎn)悠的錯誤,即并發(fā)清楚線程和工作線程同時工作,清理出來老年代的空間不足以存放由新生代晉升到老年代的對象。
解決措施:減少年輕代大小,避免放入老年代時需要分配大的空間,同時調(diào)整觸發(fā)Full GC時的比率以及將觸發(fā)CMS GC的閥值適當(dāng)增大
5. JVM中四種引用你有了解過嗎?
- 強引用:垃圾收集器不會回收被強引用的對象。
在 Java 中最常見的就是強引用, 把一個對象賦給一個引用變量,這個引用變量就是一個強引用。即在我們寫類似這樣User user = new User()
,我們new出來的user
對象就是一個強引用了!
當(dāng)一個對象被強引用變量引用時,它處于可達狀態(tài),它是不可能被垃圾回收機制回收的即使在內(nèi)存不足的情況下,JVM寧愿拋出OutOfMemory
錯誤也不會回收這種對象。
- 軟引用:在沒有被強引用對象,當(dāng)系統(tǒng)要發(fā)生內(nèi)存溢出的異常之前,會將其列為回收范圍,進行第二次回收。
軟引用需要用 SoftReference 類來實現(xiàn),對于只有軟引用的對象來說,當(dāng)系統(tǒng)內(nèi)存足夠時它不會被回收,當(dāng)系統(tǒng)內(nèi)存空間不足時它會被回收。軟引用通常用在對內(nèi)存敏感的程序中。
- 弱引用:具有弱引用的對象擁有更短暫的生命周期。在沒有被強引用對象,只能存活在下一次垃圾收集器前。無論內(nèi)存夠不夠。
弱引用需要用 WeakReference 類來實現(xiàn),它比軟引用的生存期更短,對于只有弱引用的對象來說,只要垃圾回收機制一運行,不管 JVM 的內(nèi)存空間是否足夠,總會回收該對象占用的內(nèi)存。
- 虛引用:無法通過虛引用取得一個對象實例,設(shè)置虛引用的目的是為了能在這個對象被垃圾收集器回收時收到一個通知。 虛引用的主要作用是跟蹤對象被垃圾回收的狀態(tài)。
6. 說說你知道的幾種主要的JVM參數(shù)
1.堆設(shè)置
- -Xms:初始堆大小
- -Xmx:最大堆大小
- -XX:NewSize=n:設(shè)置新生代大小
- **-XX:NewRatio=n:**設(shè)置年輕代和年老代的比值。如:為3,表示新生代與老年代比值為1:3,新生代占整個新生代老年代和的1/4
- -XX:SurvivorRatio=n:新生代中Eden區(qū)與兩個Survivor區(qū)的比值。注意Survivor區(qū)有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區(qū)占整個新生代的1/5
- -XX:MaxPermSize=n:設(shè)置持久代大小
2.收集器設(shè)置
- -XX:+UseSerialGC:設(shè)置串行收集器
- -XX:+UseParallelGC:設(shè)置并行收集器
- -XX:+UseParalledlOldGC:設(shè)置并行老年代收集器
- -XX:+UseConcMarkSweepGC:設(shè)置并發(fā)收集器
3.并行收集器設(shè)置
- -XX:ParallelGCThreads=n:設(shè)置并行收集器收集時使用的CPU數(shù)。并行收集線程數(shù)。
- -XX:MaxGCPauseMillis=n:設(shè)置并行收集最大暫停時間
- -XX:GCTimeRatio=n:設(shè)置垃圾回收時間占程序運行時間的百分比。公式為1/(1+n)
4.并發(fā)收集器設(shè)置
- -XX:+CMSIncrementalMode:設(shè)置為增量模式。適用于單CPU情況。
- -XX:ParallelGCThreads=n:設(shè)置并發(fā)收集器新生代收集方式為并行收集時,使用的CPU數(shù)。并行收集線程數(shù)。
5.JVM 調(diào)優(yōu)的參數(shù)
- **-Xms2g:**初始化推大小為 2g;
- **-Xmx2g:**堆最大內(nèi)存為 2g;
- **-XX:NewRatio=4:**設(shè)置年輕的和老年代的內(nèi)存比例為 1:4;
- **-XX:SurvivorRatio=8:**設(shè)置新生代 Eden 和 Survivor 比例為 8:2;
- **–XX:+UseParNewGC:**指定使用 ParNew + Serial Old 垃圾回收器組合;
- **-XX:+UseParallelOldGC:**指定使用 ParNew + ParNew Old 垃圾回收器組合;
- **-XX:+UseConcMarkSweepGC:**指定使用 CMS + Serial Old 垃圾回收器組合;
- **-XX:+PrintGC:**開啟打印 gc 信息;
- **-XX:+PrintGCDetails:**打印 gc 詳細信息。
總結(jié)
本篇文章就到這里了,如果這篇文章對你也有所幫助,希望您可以多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
javax.validation包里@NotNull等注解的使用方式
這篇文章主要介紹了javax.validation包里@NotNull等注解的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01SpringBoot通過@MatrixVariable進行傳參詳解
這篇文章主要介紹了SpringBoot使用@MatrixVariable傳參,文章圍繞@MatrixVariable展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06IntelliJ IDEA 部署 Web 項目,看這一篇夠了!
這篇文章主要介紹了IntelliJ IDEA 部署 Web 項目的圖文教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05SpringBoot實現(xiàn)多數(shù)據(jù)源的切換實踐
這篇主要介紹了SpringBoot實現(xiàn)多數(shù)據(jù)源的切換,本文基于AOP來實現(xiàn)數(shù)據(jù)源的切換,文中通過示例代碼介紹的非常詳細,感興趣的小伙伴們可以參考一下2022-03-03Springboot整合JPA配置多數(shù)據(jù)源流程詳解
這篇文章主要介紹了Springboot整合JPA配置多數(shù)據(jù)源,JPA可以通過實體類生成數(shù)據(jù)庫的表,同時自帶很多增刪改查方法,大部分sql語句不需要我們自己寫,配置完成后直接調(diào)用方法即可,很方便2022-11-11Java反射機制原理、Class獲取方式以及應(yīng)用場景詳解
反射機制是JAVA的核心知識點之一,大多數(shù)框架的實現(xiàn)原理就是利用了反射機制,掌握反射機制會使你學(xué)習(xí)框架更加輕松高效,這篇文章主要給大家介紹了關(guān)于Java反射機制原理、Class獲取方式以及應(yīng)用場景的相關(guān)資料,需要的朋友可以參考下2022-04-04