天天看點

JVM記憶體分區1 堆2 方法區3 虛拟機棧4 本地方法棧5 程式計數器 

1 堆

        Java堆是所有線程所共享的一塊記憶體,在虛拟機啟動時建立,幾乎所有的對象執行個體都在這裡建立,是以該區域經常發生垃圾回收操作。需要留意的是,并不是所有的對象都是配置設定在堆中,後面會說明。

2 方法區

        在方法區進行的GC主要是對方法區裡的常量池和類型的解除安裝。方法區主要用來存儲已被虛拟機加載的類的中繼資料資訊、常量池、靜态變量和即時編輯器編譯後的代碼等資料。該區域是被線程共享的。方法區裡有一個運作時常量池,用于存放靜态編譯産生的字面量和符号引用。

        Java 8之前,方法區的實作是永久代。但是從Java 8開始,HotSpot虛拟機中已經沒有永久代了,取而代之的是元空間(Metaspace)。元空間和永久代類似,都是對JVM規範中方法區的一種實作。類的中繼資料被放入到元空間中,而字元串常量和類的靜态變量則被移動至Java堆中。這樣做可以使加載多少類的中繼資料都不由MaxPermSize控制,而由系統的實際可用空間來控制。減少OOM的産生,并且為HotSpot和JRockit虛拟機的融合做出努力(兩者合并的結果即“HotRockit”項目隻能說差強人意。HotSpot除了用本地記憶體代替永久代實作方法區之外,包括JMC、JFR和NMT等功能都是從JRockit借鑒過來的。其他功能由于兩者架構的差異性明顯,HotSpot能夠直接借鑒融合的功能寥寥無幾)。

3 虛拟機棧

        虛拟機棧也就是我們平常所稱的棧記憶體,它為Java方法服務,每個方法在執行的時候都會建立一個棧幀,用于存儲局部變量表、操作數棧、動态連結和方法出口等資訊。虛拟機棧是線程私有的,它的生命周期與線程相同。

        局部變量表裡存儲的是基本資料類型、returnAddress類型(指向一條位元組碼指令的位址)和對象引用,這個對象引用有可能是指向對象起始位址的一個指針,也有可能是代表對象的句柄或者與對象相關聯的位置。局部變量所需的記憶體空間在編譯期間确定。

        操作數棧的作用主要用來存儲運算結果以及運算的操作數,它不同于局部變量表通過索引來通路,而是壓棧和出棧的方式。

        每個棧幀都包含一個指向運作時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法調用過程中的動态連結。動态連結就是将常量池中的符号引用在運作期轉化為直接引用,将main方法以及靜态方法替換成指向資料所存記憶體的指針。

        方法出口指的是儲存方法跳出的代碼點,該方法裡面執行到子方法時,會再開辟一個子方法的棧幀,等子方法執行完畢、銷毀這個棧幀後,需要再跳回到父方法跳出的代碼點,這時方法出口就記錄了這個跳出點。

        而JVM的運作模式有三種:

  • 解釋模式:執行一行位元組碼就編譯為一行機器碼;
  • 編譯模式:先将整個位元組碼檔案全部編譯成機器碼,然後一次性執行;
  • 混合模式:大體上使用解釋模式來執行,但對于一些熱點代碼采取編譯模式來執行,這也是預設的運作模式。

        混合模式兼顧了解釋模式啟動速度快的優點,以及編譯模式後期運作速度快的特點,因為一些熱點代碼所對應的機器碼會被緩存起來,下次再執行的時候無需再編譯,也就是JIT即時編譯技術。

        在JIT即時編譯的過程中會去優化我們寫過的代碼,比如鎖消除技術。去除不可能存在的競争共享資源的鎖,以此來節省沒必要的請求鎖時間。還有逃逸分析技術。如果一些對象隻在方法裡面存活,并且沒有傳回return出去,那麼有可能這些對象不會在堆中被配置設定記憶體,而是在棧中,以此來分擔堆配置設定記憶體的壓力。但這也不是絕對會發生的。

4 本地方法棧

        本地方法棧和虛拟機棧類似,隻不過本地方法棧執行的都是native方法。

5 程式計數器 

        記憶體空間小,位元組碼解釋器工作時通過改變這個計數值可以選取下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理和線程恢複等功能都需要依賴這個計數器完成。該記憶體區域是唯一一個Java虛拟機規範沒有規定任何OOM情況發生的區域。