Java必考题目之JVM面试题目和答案
JVM內(nèi)存模型
首先我們來了解一下JVM的內(nèi)存模型的怎么樣的:
1.堆:存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存
- 堆得內(nèi)存由-Xms指定,默認(rèn)是物理內(nèi)存的1/64;最大的內(nèi)存由-Xmx指定,默認(rèn)是物理內(nèi)存的1/4。
- 默認(rèn)空余的堆內(nèi)存小于40%時,就會增大,直到-Xmx設(shè)置的內(nèi)存。具體的比例可以由-XX:MinHeapFreeRatio指定
- 空余的內(nèi)存大于70%時,就會減少內(nèi)存,直到-Xms設(shè)置的大小。具體由-XX:MaxHeapFreeRatio指定。
2.虛擬機棧
虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態(tài)鏈接、方法出口等信息本地方法棧:本地方法棧則是為虛擬機使用到的Native方法服務(wù)。
3.方法區(qū):存儲已被虛擬機加載的類元數(shù)據(jù)信息(元空間)
1)有時候也成為永久代,在該區(qū)內(nèi)很少發(fā)生垃圾回收,但是并不代表不發(fā)生GC,在這里進行的GC主要是對方法區(qū)里的常量池和對類型的卸載
2)方法區(qū)主要用來存儲已被虛擬機加載的類的信息、常量、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)。
該區(qū)域是被線程共享的。
3)方法區(qū)里有一個運行時常量池,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號引用。該常量池具有動態(tài)性,也就是說常量并不一定是編譯時確定,運行時生成的常量也會存在這個常量池中。
4.程序計數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器
總結(jié):
JVM垃圾回收算法
1.標(biāo)記-清除: 這是垃圾收集算法中最基礎(chǔ)的,根據(jù)名字就可以知道,它的思想就是標(biāo)記哪些要被回收的對象,然后統(tǒng)一回收。這種方法很簡單,但是會有兩個主要問題:1.效率不高,標(biāo)記和清除的效率都很低;2.會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導(dǎo)致以后程序在分配較大的對象時,由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次GC動作。?
2.復(fù)制算法: 為了解決效率問題,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊,當(dāng)一塊內(nèi)存用完時,就將還存活的對象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存,再將第二塊上的對象復(fù)制到第一塊。但是這種方式,內(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ū)滿,就將對象復(fù)制到第二塊內(nèi)存區(qū)上,然后清除Eden區(qū),如果此時存活的對象太多,以至于Survivor不夠時,會將這些對象通過分配擔(dān)保機制復(fù)制到老年代中。(java堆又分為新生代和老年代)
3. 標(biāo)記-整理 該算法主要是為了解決標(biāo)記-清除,產(chǎn)生大量內(nèi)存碎片的問題;當(dāng)對象存活率較高時,也解決了復(fù)制算法的效率問題。它的不同之處就是在清除對象的時候現(xiàn)將可回收對象移動到一端,然后清除掉端邊界以外的對象,這樣就不會產(chǎn)生內(nèi)存碎片了。?
4.分代收集 現(xiàn)在的虛擬機垃圾收集大多采用這種方式,它根據(jù)對象的生存周期,將堆分為新生代和老年代。在新生代中,由于對象生存期短,每次回收都會有大量對象死去,那么這時就采用復(fù)制算法。老年代里的對象存活率較高,沒有額外的空間進行分配擔(dān)保,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除。
JVM垃圾收集器有哪些?以及優(yōu)劣勢比較?
1.串行收集器??
串行收集器是最簡單的,它設(shè)計為在單核的環(huán)境下工作(32位或者windows),你幾乎不會使用到它。它在工作的時候會暫停整個應(yīng)用的運行,因此在所有服務(wù)器環(huán)境下都不可能被使用。
使用方法:-XX:+UseSerialGC
2.并行收集器
這是JVM默認(rèn)的收集器,跟它名字顯示的一樣,它最大的優(yōu)點是使用多個線程來掃描和壓縮堆。缺點是在minor和full GC的時候都會暫停應(yīng)用的運行。并行收集器最適合用在可以容忍程序停滯的環(huán)境使用,它占用較低的CPU因而能提高應(yīng)用的吞吐(throughput)。
使用方法:-XX:+UseParallelGC
3.CMS收集器
CMS是Concurrent-Mark-Sweep的縮寫,并發(fā)的標(biāo)記與清除。
這個算法使用多個線程并發(fā)地(concurrent)掃描堆,標(biāo)記不使用的對象,然后清除它們回收內(nèi)存。在兩種情況下會使應(yīng)用暫停(Stop the World, STW):
1. 當(dāng)初次開始標(biāo)記根對象時initial mark。
2. 當(dāng)在并行收集時應(yīng)用又改變了堆的狀態(tài)時,需要它從頭再確認(rèn)一次標(biāo)記了正確的對象final remark。
這個收集器最大的問題是在年輕代與老年代收集時會出現(xiàn)的一種競爭情況(race condition),稱為提升失敗promotion failure。對象從年輕代復(fù)制到老年代稱為提升promotion,但有時侯老年代需要清理出足夠空間來放這些對象,這需要一定的時間,它收集的速度可能趕不上不斷產(chǎn)生的要提升的年輕代對象的速度,這時就需要做STW的收集。STW正是CMS想避免的問題。為了避免這個問題,需要增加老年代的空間大小或者增加更多的線程來做老年代的收集以趕上從年輕代復(fù)制對象的速度。
除了上文所說的內(nèi)容之外,CMS最大的問題就是內(nèi)存空間碎片化的問題。CMS只有在觸發(fā)FullGC的情況下才會對堆空間進行compact。如果線上應(yīng)用長時間運行,碎片化會非常嚴(yán)重,會很容易造成promotion failed。為了解決這個問題線上很多應(yīng)用通過定期重啟或者手工觸發(fā)FullGC來觸發(fā)碎片整理。
對比并行收集器它的一個壞處是需要占用比較多的CPU。對于大多數(shù)長期運行的服務(wù)器應(yīng)用來說,這通常是值得的,因為它不會導(dǎo)致應(yīng)用長時間的停滯。但是它不是JVM的默認(rèn)的收集器。
4.G1收集器
如果你的堆內(nèi)存大于4G的話,那么G1會是要考慮使用的收集器。它是為了更好支持大于4G堆內(nèi)存在JDK 7 u4引入的。G1收集器把堆分成多個區(qū)域,大小從1MB到32MB,并使用多個后臺線程來掃描這些區(qū)域,優(yōu)先會掃描最多垃圾的區(qū)域,這就是它名稱的由來,垃圾優(yōu)先Garbage First。
如果在后臺線程完成掃描之前堆空間耗光的話,才會進行STW收集。它另外一個優(yōu)點是它在處理的同時會整理壓縮堆空間,相比CMS只會在完全STW收集的時候才會這么做。
使用過大的堆內(nèi)存在過去幾年是存在爭議的,很多開發(fā)者從單個JVM分解成使用多個JVM的微服務(wù)(micro-service)和基于組件的架構(gòu)。其他一些因素像分離程序組件、簡化部署和避免重新加載類到內(nèi)存的考慮也促進了這樣的分離。
除了這些因素,最大的因素當(dāng)然是避免在STW收集時JVM用戶線程停滯時間過長,如果你使用了很大的堆內(nèi)存的話就可能出現(xiàn)這種情況。另外,像Docker那樣的容器技術(shù)讓你可以在一臺物理機器上輕松部署多個應(yīng)用也加速了這種趨勢。
使用方法:-XX:+UseG1GC
你可能也喜歡:
總結(jié)
以上是生活随笔為你收集整理的Java必考题目之JVM面试题目和答案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 会议交流 | 智能风控技术峰会(请关注图
- 下一篇: 史上最全java架构师技能图谱(上)