天天看點

JVM(二)JVM記憶體結構研究

 一、JVM記憶體結構概覽

  由上圖可以看到,一個程式在運作時,JVM資料區主要有這幾塊組成部分,且每一塊組成部分都可以通過設定對應的虛拟機參數來進行調優。

  Spring Boot應用包運作時可以通過以下格式的指令進行啟動(Tomcat啟動直接加在bin目錄下catalina.sh檔案裡):

java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar 應用程式包名.jar      

  PS:-Xss設定越小時,一個線程棧裡能配置設定的棧幀就越少【循環計算的次數越少】,但是對JVM整體來說能開啟的線程數會更多。

二、舉一個栗子

三、JVM内部做了啥呢?~

  操作數棧:操作數在程式運作期間做操作時,臨時存放的一塊記憶體空間。

  動态連結:Java Class檔案中有很多符号引用,一部分在類加載的時候轉化為直接引用,另一部分在運作期間轉化為直接引用,這部分被稱為動态連結。【符号引用轉直接引用的過程】

  方法出口:當一個方法執行的時候,隻有兩種可以退出方法的方法:

  • 正常完成出口:JVM碰到任意一個方法傳回的位元組碼指令。
  • 異常完成出口:在執行方法中抛出異常并且未對異常進行處理。

  PS:方法退出的時候相當于把棧幀出棧。

  PS:棧内若有對象類型的局部變量,則局部變量表内部存放的是該對象在堆上的記憶體位址,而非對象本身。

  PS:元空間使用的是我們記憶體條的實體記憶體。(jdk1.8以後才叫元空間,jdk1.8之前叫永久代)

元空間

  關于元空間的JVM參數有兩個:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N

  • -XX:MaxMetaspaceSize:設定元空間最大值, 預設是-1, 即不限制, 或者說隻受限于本地記憶體大小。
  • -XX:MetaspaceSize:指定元空間觸發Fullgc的初始門檻值(元空間無固定初始大小), 以位元組為機關,預設是21M,達到該值就會觸發full gc進行類型解除安裝, 同時收集器會對該值進行調整:
    • 如果釋放了大量的空間,就适當降低該值
    • 如果釋放了很少的空間, 那麼在不超過-XX:MaxMetaspaceSize(如果設定了的話)的情況下, 适當提高該值

  PS:1.7jdk版本的-XX:PermSize參數代表永久代的初始容量。 由于調整元空間的大小需要進行Full GC,這是非常昂貴的操作,如果應用在啟動的時候發生大量Full GC,通常都是由于永久代(jdk1.7及以前)或元空間(jdk1.8+)發生了大小調整【可能會導緻項目啟動時間過長】,基于這種情況,一般建議在JVM參數中将MetaspaceSize和MaxMetaspaceSize設定成一樣的值,并設定得比初始值要大, 對于8G實體記憶體的機器來說,一般将這兩個值都設定為256M。

四、垃圾回收(GC)機制圖解【存活對象的活動路徑】

  JVM内部有一個自己的垃圾回收機制【針對堆中的對象進行回收】,把沒用的對象進行釋放換取記憶體,我們通過一個流程圖來簡單的看一下JVM在堆中是怎麼操作對象并進行GC操作的:

STW

  在GC的時候有一個STW【stop the world】的概念,就是在GC進行時,JVM會暫時停止所有正在運作的線程,來進行垃圾對象的查找,此時使用者通路程式時就會有感覺網絡卡頓加載不出來的使用者體驗。

  PS:之是以這樣設計,是因為每一次Full GC的消耗很大,如果不先把所有線程停止,此時可能會産生這種情況:

  • 當GC查找到對象A的時候,發現對象A還在被線程使用,此時JVM不會回收對象A及對象A引用的一連串對象。
  • 當GC開始檢查下一個對象的時候,對象A的線程已經結束,此時對象A和其引用的對象已經變成垃圾對象,GC卻沒有回收掉,就會白白浪費很多時間,GC的效果也極其不好。

五、小問答

Q:類對象和類資訊的差別

  類對象一般存在于堆中,主要是通過類執行個體化之後産生的。類資訊則存放在方法區(元空間)裡,主要内容是類的一些常量池,位元組碼檔案等内容。