「JVM 系列」- 帶你搞懂JVM之運行時數據區域

這是我參與8月更文挑戰的第4天,活動詳情查看:8月更文挑戰html

前言

JVM(Java Virtual Machine):爪哇虛擬機java

JVM內存結構:運行時數據區域git

事實上,對於JVM上層,還存在一個由Oracle定製的Java虛擬機規範,咱們常說的JVM通常是指具體的某個JVM規範的具體實現。好比咱們常用的Java虛擬機HotSpot,JDK1.8使用的就是HotSpotweb

> java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
複製代碼

好比咱們常常搞混的,JDK1.8以前的永久代、JDK1.8以後的元空間,這篇文章是永久代,到了那篇文章又是元空間的,實際上他們兩個均可以看做是JVM規範中方法區的實現數組

本文針對JVM的內存區域,儘量詳細的解剖緩存

事實上,JVM的知識多且雜,容易混淆,對此,我強烈建議你點贊收藏加關注,逛完P B站,趁着精神狀態良好,來複習一遍JVM想必也是極好服務器


JVM總體架構

JVM內存區域,又稱運行時數據區域,就是下圖紅框框起來的部分:markdown

JVM總體架構圖:數據結構


JVM內存結構

主要分爲如下區域:架構

  1. 線程私有的:程序計數器,本地方法棧,虛擬機棧
  2. 線程公有的:方法區,堆

程序計數器(線程私有)

程序計數器是一塊較小的內存空間,能夠看做是當前線程所執行的字節碼的**行號指示器;**它是惟一一個在 JVM 規範中沒有規定任何 OutOfMemoryError 狀況的區域

程序計數器的做用

當多個線程在特定的時間內同時執行的時候,一個線程不可能一直佔用着CPU 的資源,這樣就會形成頻繁的上下文切換,爲了線程切換回來後,能恢復到正確的位置繼續執行,每一個線程都有本身的程序計數器

字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,從而實現代碼的流程控制,如:順序執行、選擇、循環、異常處理等


程序計數器裏存儲的是什麼?

任什麼時候間一個線程都只有一個方法在執行,也就是所謂的當前方法。若是當前線程正在執行的是 Java 方法,程序計數器記錄的是 JVM 字節碼指令地址,若是是執行 native 方法,則是未指定值(undefined)


虛擬機棧(線程私有)

每一個線程在建立的時候都會建立一個虛擬機棧,其內部保存一個個的棧幀,每一次方法調用都會有一個對應的棧幀被壓入棧中,每個函數調用結束後,都會有一個棧幀被彈出,遵循「先進後出/後入先出」的原則

以下圖,方法1調用了方法2,因而方法2把方法1壓在了身下,這個過程就是壓棧;當前方法執行完畢後(return或者異常),就會進行出棧操做


棧裏面存什麼

虛擬機棧裏面保存棧幀,每一個棧幀中保存了方法的局部變量表、操做數棧、動態連接、方法出口信息

在這裏大概說一下棧裏面的幾個東西是幹嗎的,詳細內容下回分解

  • 局部變量表:一組變量值存儲空間,主要用於存儲方法參數和定義在方法體內的局部變量,好比方法內的String abc = "abc";對象引用(reference 類型,它不一樣於對象自己,多是一個指向對象起始地址的引用指針,也多是指向一個表明對象的句柄或其餘與此對象相關的位置)
  • 操做數棧:主要用於保存計算過程的中間結果,同時做爲計算過程當中變量臨時的存儲空間
  • 動態連接:每一個棧幀都保存了一個能夠指向當前方法所在類的運行時常量池, 目的是: 當前方法中若是須要調用其餘方法的時候, 可以從運行時常量池中找到對應的符號引用, 而後將符號引用轉換爲直接引用,而後就能直接調用對應方法, 這就是動態連接
  • 方法返回地址:方法正常return退出時,調用者的 PC 計數器的值做爲返回地址,即調用該方法的指令的下一條指令的地址;經過異常退出的,返回地址是要經過異常表來肯定的,棧幀中通常不會保存這部分信息

虛擬機棧會拋出什麼異常

虛擬機棧的內存大小能夠是動態的,也能夠是固定大小的

  • StackOverFlowError:當設置爲固定大小時,如果壓棧超出了最大的容許值就會拋出該異常
  • OutOfMemoryError:當設置爲動態擴展時,如果擴展時沒法向系統申請到足夠的內存時會報該異常

### 本地方法棧(線程私有)

本地方法棧的功能與虛擬機棧相似,會拋出的異常也同樣,二者的區別在於,虛擬機棧是爲Java方法服務的,而本地方法棧是爲Native方法服務的

並非全部 JVM 都支持本地方法。由於 Java 虛擬機規範並無明確要求本地方法棧的使用語言、具體實現方式、數據結構等。若是 JVM 產品不打算支持 native 方法,也能夠無需實現本地方法棧

在 Hotspot JVM 中,直接將本地方法棧和虛擬機棧合二爲一


堆(線程共享)

Java 虛擬機所管理的內存中最大的一塊,Java 堆是全部線程共享的一塊內存區域,在虛擬機啓動時建立。此內存區域的惟一目的就是存放對象實例幾乎全部的對象實例以及數組都在這裏分配內存,正因如此,Java 堆也是垃圾收集器管理的主要區域,所以也被稱做GC 堆(Garbage Collected Heap)

堆內存的劃分

爲了高效的進行垃圾回收,能夠發現,堆被劃分紅了三個區域:

  1. 新生代(Young Generation):新對象和沒達到必定年齡的對象都在新生代,新生代又被分爲Eden、Survivor0,Survivor1
  2. 老年代(Old Generation):被長時間使用的對象,年齡到了之後會進入老年代,老年代的內存空間應該要比年輕代更大
  3. 元空間(圖中的Perm區):JDK1.8之前不叫元空間,叫永久代,無論是元空間仍是永久代,均可以看做是JVM規範中方法區的實現。方法區有個別名叫非堆(Non-Heap),目的應該是與 Java 堆區分開


如何配置堆內存

在啓動Java程序的時候,能夠用參數指定堆的大小:

  • -Xms:設定堆的起始內存,默認狀況下,初始堆內存大小爲:電腦內存大小/64
  • -Xmx:設定堆的最大內存,默認狀況下,最大堆內存大小爲:電腦內存大小/4

分享一個小zi si,咱們一般會將 -Xmx-Xms 兩個參數配置爲相同的值,其目的是爲了可以在垃圾回收機制清理完堆區後再也不須要從新分隔計算堆的大小,從而提升性能

例:

java -Xmx1024m -Xms1024m -jar test.jar
複製代碼

堆最容易出現 OutOfMemoryError錯誤,有兩個比較常見的錯誤信息:

  1. OutOfMemoryError: GC Overhead Limit Exceeded:這是JVM在垃圾回收時用了太多時間而且回收的空間不多的時候會報出的錯誤
  2. OutOfMemoryError: Java heap space:這是建立對象是,發現堆中的內存不足以存放新的對象就會出現這個報錯

經過對象在堆中的生命週期進一步瞭解堆

  1. 當建立了一個新的對象時,首先會在新生代的Eden區分配內存,此時JVM會給這個對象一個年齡(能夠經過-XX:MaxTenuringThreshold參數設置)
  2. 當新來的對象很大,而Eden空間不足時,就會觸發Minor GC,把不須要的幹掉,再將新對象放入Eden區
  3. Eden經歷一次垃圾回收後的對象會被放入Survivor0中,而且給它的年齡+1
  4. 若是再次觸發垃圾回收,Survivor0 中的對象會被移入 Survivor1,年齡再次+1
  5. 若是對象的年齡超過了咱們配置的進入老年代的年齡閾值,對象就會被分配到老年代
  6. 老年代不多進行GC,當老年代內存不足時,會觸發MajorGC,若到了這一步依然沒法對新來的對象進行保存,就會出現OOM(內存溢出)

方法區

方法區,是JVM規範定義的一個概念,不管是JDK1.8以前的永久代,仍是JDK1.8以後的元空間,都屬於規範的一種是實現。能夠把方法區看做Java代碼中的接口,而永久代和元空間則是它的具體實現

方法區用於存儲已被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等

如何配置元空間的大小

  1. -XX:MetaspaceSize:設置元空間的初始大小
  2. -XX:MaxMetaspaceSize:設置元空間的最大大小

因爲元空間是直接使用系統的物理內存的,若是不指定元空間額度大小,隨着類的建立,虛擬機可能會耗盡全部的可用系統內存

一樣的,因爲元空間使用的是系統內存,雖然任有可能溢出,可是出現的機率會小不少,當元空間溢出時會報錯 OutOfMemoryError: MetaSpace

須要注意的是:

  • 對於一個 64 位的服務器端 JVM 來講,其默認的 -XX:MetaspaceSize 的值爲20.75MB,這就是初始的高水位線,一旦觸及這個水位線,Full GC 將會被觸發並卸載沒用的類(即這些類對應的類加載器再也不存活),而後這個高水位線將會重置,新的高水位線的值取決於 GC 後釋放了多少元空間。若是釋放的空間不足,那麼在不超過 MaxMetaspaceSize時,適當提升該值。若是釋放空間過多,則適當下降該值
  • 若是初始化的高水位線設置太低,上述高水位線調整狀況會發生不少次,經過垃圾回收的日誌可觀察到 Full GC 屢次調用。爲了不頻繁 GC,能夠將 -XX:MetaspaceSize 設置爲一個相對較高的值。

Reference

JVM 基礎 - JVM 內存結構 | Java 全棧知識體系 (pdai.tech)

JavaGuide - Java內存區域

CSDN:什麼叫作JVM與JVM規範

相關文章
相關標籤/搜索