<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    學完了這篇JVM,面試官真拿我沒辦法了!

    在我們面試中經常會遇到面試官問一些有關JVM的問題,下面我大概從運行時數據域、類加載機制、類加載器、垃圾收集器、垃圾收集算法、JVM堆內存模型、JVM內存結構、JVM調優等幾個方面來講一下JVM。

    一、運行時數據區域

    在執行Java程序的時候,JAVA虛擬機會將自己所管理的內存劃分為若干個不同的數據區域,每個區域分工不同,這些區域統稱為“運行時數據區域”。下面來根據一張圖來看一下這幾個區域。 file 1、程序計數器

    1>較小的內存空間。

    2>當前線程字節碼的行號指示器。

    3>改變計數器的值來選取下一條需要執行的字節碼指令。

    4>一個處理器只會執行一條線程中的指令,為了線程切換后能恢復到正確的執行位置,每個線程一個計數器。

    2、Java虛擬機棧

    1>線程私有。

    2>生命周期與線程相同。

    3>每個方法執行都會創建棧幀,用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。

    3、本地方法棧

    1>線程私有。

    2>使用到的本地(Native)方法服務。

    4、Java堆

    1>內存中最大的一塊。

    2>線程共享。

    3>堆滿了拋出OutOfMemoryError異常。

    5、方法區

    1>線程共享。

    2>用于存儲已被虛擬機加載的類型信息、常量、靜態變量、及時編譯器編譯后的代碼緩存等數據。

    6、運行時常量池(次區域在方法區內部)

    1>運行時常量池是方法區的一部分。

    2>存放各種字面量與符號引用。

    二、垃圾收集算法

    1、標記-清除算法

    首先標記出所有需要回收的對象,在標記完成后,統一回收掉所有被標記的對象。如圖。

    file

    2、標記-復制算法

    將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活的對象復制到另一塊上面,然后再把已使用過的內存空間一次清理掉。如圖。

    file

    3、標記-整理算法

    標記-整理算法就是標記-清除算法后再將存活對象整理到一起,從而騰出來更大的連續空間。如圖。

    file

    三、垃圾收集器

    1、Serial收集器

    file

    Serial收集器是最基礎、歷史最悠久的收集器,這個收集器是一個單線程工作的收集器。

    2、ParNew收集器

    file

    ParNew收集器實質上是Serial收集器的多線程并行版本,可以同時使用多條線程進行垃圾收集。

    3、Parallel Scavenge 收集器

    Parallel Scavenge收集器是一款新生代收集器,它是基于標記-復制算法實現的收集器。Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量,所謂吞吐量就是處理器用于運行用戶代碼的時間與處理器總消耗時間的比值,即。

    file

    4、Serial Old收集器

    file

    Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器。

    5、Parallel Old收集器

    Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多線程并行收集,基于標記-整理算法實現。

    file

    6、CMS收集器

    file

    CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,它的運作過程分為四個步驟,包括:

    1> 初始標記

    2> 并發標記

    3> 重新標記

    4> 并發清除

    初始標記:初始標記需要stw,初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快。

    并發標記:并發標記階段就是從GC Roots的直接關聯對象開始遍歷整個對象圖的過程,這個過程耗時較長但是不需要停頓用戶線程,可以與垃圾收集線程一起并發運行。

    重新標記:重新標記階段則是為了修正并發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但也遠比并發標記階段的時間短。

    并發清除:這個階段清理刪除掉標記階段判斷的已經死亡的對象,由于不需要移動存活對象,所以這個階段也是可以與用戶線程同時并發的。

    6、Garbage First 收集器(簡稱G1收集器)

    G1不再堅持固定大小以及固定數量的分代區域劃分,而是把連續的Java堆劃分為多個大小相等的獨立區域(Region),每一個Region都可以根據需要,扮演新生代的Eden空間、Survivor空間,或者老年代空間。如圖。

    file

    G1收集器的運作過程大致可以劃分為以下四個步驟:

    初始標記:僅僅只是標記一下GC Roots能直接關聯到的對象,

    并發標記:從GC Root開始對堆中對象進行可達性分析,遞歸掃描整個堆里的對象圖,找出要回收的對象,這階段耗時較長,但可與用戶程序并發執行。

    最終標記:對用戶線程做另一個短暫的暫停,用于處理并發階段結束后仍遺留下來的最后那少量的SATB記錄。

    篩選回收:負責更新Region的統計數據,對各個Region的回收價值和成本進行排序,根據用戶所期望的停頓時間來制定回收計劃,可以自由選擇任意多個Region構成回收集,然后把決定回收的那一部分Region的存活對象復制到空的Region中,再清理掉整個舊Region的全部空間。這里的操作設計存活對象的移動,是必須暫停用戶線程,由多條收集器線程并行完成的。

    file

    四、類加載機制

    當我們運行一個類的時候,我們會通過java.exe來調用jvm.dll文件,創建一個類加載器,然后再加載我們的類,我們的類在加載的過程中會經歷加載、驗證、準備、解析、初始化。

    file

    加載:此階段將類的二進制字節流加載到內存中。

    驗證:此階段將驗證文件的格式、元數據驗證、字節碼驗證、符號應用驗證,保證這些代碼運行后不會危害虛擬機自身的安全。

    準備:準備階段是為類中定義的變量(即靜態變量,被static修飾的變量)分配內存并設置類變量初始默認值。

    解析:解析階段是Java虛擬機將常量池內的符號引用替換為直接引用的過程,符號引用是用符號來描述所引用的目標,而直接引用是用地址來描述所應用的目標。

    初始化:由于之前準備階段是給靜態變量賦值默認值,而初始化階段是給靜態變量賦值他的真實的值。

    五、類加載器

    1、啟動類加載器:這個類加載器負責加載存放在 \lib目錄中的系統的jar。

    2、擴展類加載器:這個類加載器負責加載存放在 \lib\ext目錄中的擴展類jar。

    3、應用程序類加載器:這個類加載器負責加載我們自己寫的類。

    六、雙親委派模型

    file

    前面我們說到每個類會有一個對應的類加載器去加載這個類,而不同的類加載器所加載的類的類型不同,當我們要加載一個類的時候首先會去應用程序類加載器加載過的集合里(注:這里是加載過的集合里)查看有沒有加載這個類,如果沒有就去擴展類加載器加載過的集合里查看有沒有加載過這個類,如果沒有就再向上去找引導類加載器加載過的集合里看有沒有加載過這個類,如果也沒有就會從引導類加載器要加載的核心類中尋找有沒有要加載的類,如果沒有就向下尋找擴展類加載器中要加載的擴展類中有沒有,如果也沒有就去應用程序加載器中尋找。

    好處:

    1、不會重復去加載一個類,假如應用程序類加載器加載過Student類,那么下載再加載這個類的時候只需要判斷應用程序類加載過的集合里有沒有加載過這個類,如果加載過就不用再次加載了。

    2、防止惡意修改核心類庫,比如我們自己寫一個String類,我們去運行這個類,系統會在引導類加載器中加載Java的核心類庫中的String類,而不會加載我們自定義個的String類,這就防止我們私自篡改核心類庫。

    七、JVM堆內存模型

    file

    在JVM中我們的堆內存模型大概為圖所示,我們新生成的對象會放到eden區,當我們eden區域放滿了,我們會進行一個輕GC,會把eden區域和S0區域的存活對象放到S1區域,然后將eden區域和S0區域清空,然后新生成的對象接著放到eden區域,當eden區域再次滿了,會將eden區域和S1區域的存活對象放到S0區域,然后將eden區域和S1區域清空,如此循環往復,沒進行一次循環會將沒有被清除的對象年齡+1,如果存活的對象年齡到達15(這個數值可以調整),就會將此對象放到老年代,或者是survivor區域滿了也會將一些對象放到老年代,如果當老年代滿了就會進行一次重GC。

    八、JVM調優

    所謂JVM調優主要就是減少堆內存中重GC的次數,這樣我們就根據對象什么情況會進入老年代來進行分析,首先是存活15代(這個數值可以調整),這種情況一般就是一些常量等,這些一般不會有再大的優化空間,或者一些代碼的問題造成循環調用,這種情況通過修改調整代碼即可。其次是當survivor區域滿了的話就會向老年代放入一些對象,這個時候我們就需要根據業務情況來調整堆內存的大小以及年輕代和老年代的比例,一般我們例如一些電商系統,一些訂單對象進入內存中就是朝生夕是,訂單從創建到持久化數據庫中就不在內存中使用了,所以一些正常的業務產生的對象會在輕GC的時候被收回,但是有一些大對象可能放不下年輕代中所以就會放入老年代,這種情況我們就要調大堆內存中年輕代的大小,使得一些大對象可以放入到年輕代中,并隨著輕GC被垃圾收集器收回。

    • 28
      點贊
    • 29
      評論
    • 17
      收藏
    • 一鍵三連
      一鍵三連
    • 掃一掃,分享海報

    ??2021 CSDN 皮膚主題: 1024 設計師:白松林 返回首頁
    實付
    使用余額支付
    點擊重新獲取
    掃碼支付
    錢包余額 0

    抵扣說明:

    1.余額是錢包充值的虛擬貨幣,按照1:1的比例進行支付金額的抵扣。
    2.余額無法直接購買下載,可以購買VIP、C幣套餐、付費專欄及課程。

    余額充值
    多乐彩