天天看點

淺談Jvm虛拟機Jvm虛拟機筆記

Jvm虛拟機筆記

1.類加載器、.堆、棧、

JVM整體結構

1.1類加載器:

類加載子系統(類加載器)

作用 : 類加載器(class loader)用來加載 Java 類到 Java 虛拟機中.

一般來說,Java 虛拟機使用 Java 類的方式如下:Java 源程式(.java 檔案)在經過 Java 編譯器編譯之後就被轉換成 Java 位元組代碼(.class 檔案)。類加載器負責讀取 Java 位元組代碼,并轉換成java.lang.Class類的一個執行個體。每個這樣的執行個體用來表示一個 Java 類。通過此執行個體的 newInstance()方法就可以建立出該類的一個對象。

ClassLoader的分類

Java中的ClassLoader有三種:

Bootstrap ClassLoader 、

Extension ClassLoader、

App ClassLoader。

  1. Bootstrap ClassLoader

由C++寫的,由JVM啟動.

啟動類加載器,負責加載java基礎類,對應的檔案是%JRE_HOME/lib/ 目錄下的rt.jar、resources.jar、charsets.jar和class等

2.Extension ClassLoader

Java類,繼承自URLClassLoader 擴充類加載器,

對應的檔案是 %JRE_HOME/lib/ext 目錄下的jar和class等

3.App ClassLoader

Java類,繼承自URLClassLoader 系統類加載器,

對應的檔案是應用程式classpath目錄下的所有jar和class等

1.2線程私有區域

java****棧

作用 : 線程私有,它的生命周期與線程相同。存儲局部變量, 動态連結, 方法出口等資訊.

注 : java棧也是重要内容, 内容比較多, 稍後章節詳細講解.

本地方法棧

作用 : 用于本地方法調用, JDK源碼中好多使用了Native關鍵字, 也就是調用底層C語言編寫的方法.

注意: (Sun HotSpot虛拟機)直接就把本地方法棧和Java棧合二為一.
1.2.1棧:

棧也叫棧記憶體,主管Java程式的運作,是線上程建立時建立,它的生命期是跟随線程的生命期,線程結束棧記憶體也就釋放,對于棧來說不存在垃圾回收問題,隻要線程一-結 束該棧就Over,生命周期和線程一緻,是線程私有的。8種基本類型的變量+對象的引用變量+執行個體方法都是在函數的棧記憶體中配置設定。

棧存儲什麼?

棧幀中主要儲存3類資料:

本地變量(Local Variables) :輸入參數和輸出參數以及方法内的變量;

棧操作(Operand Stack) :記錄出棧、入棧的操作;

棧幀資料(Frame Data) : 包括類檔案、方法等等。

棧的内部結構如下:

棧由一個一個棧幀組成, 棧幀中的結構又由局部變量表, 操作數棧, 幀資料區組成.

局部變量表 : 儲存函數參數,局部變量(目前函數有效,函數執行結束它銷毀)

操作數棧 : 存中間運算結果, 臨時存儲空間

幀資料區 : 儲存通路常量池指針, 異常處理表

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Vq9L804z-1605170450426)

淺談Jvm虛拟機Jvm虛拟機筆記

​ 棧模型

1.3線程共享區域

1.3.1方法區:

供各線程共享的運作時記憶體區域,它存儲了每一個類的結構資訊,例如運作時的常量池、字段和方法資料、構造方法和普通方法的位元組碼内容。方法區也叫規範,不同的虛拟機裡頭實作的不一樣,最典型的就是永久代和元空間。

實際而言,方法區(MethodArea)和堆–樣,是各個線程共享的記憶體區域,它用于存儲虛拟機加載的:類資訊+普通常量+靜态常量+編譯器編譯後的代碼等等,雖然JVM規範将方法區描述為堆的一個邏輯部分,但它卻還有一個别名叫做Non-Heap(非堆),目的就是要和堆分開。對于HotSpot虛拟機,很多開發者習慣将方法區稱之為“永久代(Parmanent Gen)” ,但嚴格本質上說兩者不同,或者說使用永久代來實作方法區而已,永久代是方法區(相當于是一個接口interface)的- -個實作,jdk1.7的版本中,已經将原本放在永久代的字元串常量池移走。

1.3.2堆:
淺談Jvm虛拟機Jvm虛拟機筆記

​ 堆模型

一個JVM執行個體隻存在一個堆記憶體,堆記憶體的大小可調節,類加載器讀取了類檔案後,需要把類、方法、常變量放到堆記憶體中,儲存所有引用類型的真實資訊,以友善執行器執行,堆記憶體分為三個部分:

  • 新生區:
  • 養老區:
  • 永久區:

堆記憶體邏輯上分為三部分:新生+養老+永久(元空間)

當Eden滿了,産生GC也叫輕GC;

當Old滿了,産生Full GC. 也叫FGC

當Full GC多次回收之後,發現養老區也沒有空間了,這時産生OOM

1.4JVM對象模型:

Java是一種面向對象的語言,而Java對象在JVM中的存儲也是有一定的結構的。而這個關于Java對象自身的存儲模型稱之為Java對象模型。

HotSpot虛拟機中(Sun JDK和OpenJDK中所帶的虛拟機,也是目前使用範圍最廣的Java虛拟機),設計了一個OOP-Klass Model。OOP(Ordinary Object Pointer)指的是普通對象指針,而Klass用來描述對象執行個體的具體類型。

每一個Java類,在被JVM加載的時候,JVM會給這個類建立一個instanceKlass對象,儲存在方法區,用來在JVM層表示該Java類。當我們在Java代碼中,使用new建立一個對象的時候,JVM會建立一個instanceOopDesc對象,這個對象中包含了對象頭以及執行個體資料。

淺談Jvm虛拟機Jvm虛拟機筆記

新生區

新生區是類的誕生、成長、消亡的區域,一一個類在這裡産生,應用,最後被垃圾回收器收集,結束生命。新生區又分為兩部分:伊甸區(Eden space) 和幸存者區(Survivor pace) , 所有的類都是在伊甸區被new出來的。幸存區有兩個:

0區(Survivor 0 space)和1區(Survivor 1 space) 。當伊甸園的空間用完時,程式又需要建立對象,JVM的垃圾回收器将對伊甸園區進行垃圾回收(MinorGC),将伊甸園區中的不再被其他對象所引用的對象進行銷毀。然後将伊甸園中的剩餘對象移動到幸存0區。若幸存0區也滿了,再對該區進行垃圾回收,然後移動到1區。那如果1區也滿了呢?再移動到養老區。若養老區也滿了,那麼這個時候将産生Ma jorGC (Fu1GC) ,進行養老區的記憶體清理。若養老區執行了Full GC之後發現依然無法進行對象的儲存,就會産生00M異常“OutOfMemoryError"。如果出現java.lang.QutOfMemoryError; Java heap space異常,說明Java虛拟機的堆記憶體不夠。原因有二:

(1)Java虛拟機的堆記憶體設定不夠,可以通過參數-Xms、-Xmx來調整。

(2)代碼中建立了大量大對象,并且長時間不能被垃圾收集器收集(存在被引用)

From區和to區位置和名分不是固定的,每次GC後會交換 誰空閑誰是to區

Java堆從GC的角度還可以細分為:新生代(Eden區、From Survivor區和To Survivor區)和老年

堆(Heap)

Young

Old

Eden

From

To

(8/10)

(1/10)(1/10)

新生代(1/3)堆空間

老年代(2/3)堆空間

MinorGC的過程(複制->清空->互換)

1: eden、 SurvivorFrom 複制到SurvivorTo,年齡+1

當産生GC回收時候,要求所有Eden區都要清空,是以那些沒有被死的對象需要将其複制到from區或者to區。年齡+1

首先,當Eden區滿的時候會觸發第一次GC,把還活 着的對象拷貝到SurvivorFrom區,當Eden .

區再次觸發GC的時候會掃描Eden區和From區域,對這兩個區域進行垃圾回收,經過這次回

收後還存活的對象,則直接複制到To區域( 如果有對象的年齡已經達到了老年的标準,則賦

值到老年代區),同時把這些對象的年齡+1

  1. 假設第一輪産生100個對象1…100住在Eden區,并且隻存活了2個對象記住A、B。那麼經過GC之前将在Eden區的存活的這兩個對象A.B複制到幸存區假設from區(to區也可以)。
  2. 當第二次又産生1…100個新對象住在Eden區的的時候,假設存活了一個對象即為C.那麼這一輪GC時候,會将這100個對象和上一輪的AB對象一起進行處理。假設這102個對象隻存活了三個對象(ABC)。那麼在GC處理之前會将這三個對象複制到目前空閑的幸存區。GC之後有交換,哪個幸存區空閑哪個是to區。由于第一輪AB對象存在from區,那麼目前空閑的幸存區即為to區。此時便将這三個對象複制到to區來。并且需要注意的是,每經過一次GC之後,存活下來的對象都會+1,在這裡也就是AB的年齡加了2,C年齡加了1.
  3. 之後重複上述步驟。。。

2:清空eden、SurvivorFrom

然後,清空Eden和SurvivorFrom中的對象, 也即複制之後有交換,誰空誰是to

B: SurvivorTo和 SurvivorFrom互換

最後,SurvivorTo和SurvivorFrom. 互換,原survivorTo成為 下一次GC時的SurvivorFrom區。部

分對象會在From和To區域中複制來複制去,如此交換15次(由JVM參數MaxTenuringThreshold

決定,這個參數預設是15),最終如果還是存活,就存入到老年代

1.5永久代、元空間、方法區

誤區:

方法區相當于一種規範,我們都知道天上飛的理念必須要有落地的實作,那麼有規範(接口)就必須有實作類,

而不同的虛拟機中實作完方法區之後,叫法不同,有的叫原空間有的實作叫永久代。

但是在jdk1.7中實作方法區中的叫永久代。

​ 在jdk1.8中實作方法區中的叫元空間。

永久代和元空間本質差別:

①:在1.7的永久代中,永久代是屬于堆記憶體中的一部分。

②:在1.8的元空間中,元空間是屬于堆外記憶體的一部分,也就是占用的是你本機擁有的實體記憶體。

永久存儲區是一個常駐記憶體區域,用于存放JDK自身所攜帶的Class, Interface的中繼資料,也就是說它存儲的是運作環境必須的類資訊,被裝載進此區域的資料是不會被垃圾回收器回收掉的,關閉JVM才會釋放此區域所占用的記憶體。

在Java8中, 永久代已經被移除,被一個稱為元空間的區域所取代。元空間的本質和永久代類似元空間與永久代之間最大的差別在于:永久帶使用的JVM的堆記憶體,但是java8以後的元空間并不在虛拟機中而是使用本機實體記憶體。是以,預設情況下,元空間的大小僅受本地記憶體限制。類的中繼資料放入native memory,字元串池和類的靜态變量放入java堆中,這樣可以加載多少類的中繼資料就不再由MaxPermSize控制,而由系統的實際可用空間來控制。

誤區: 方法區在jdk7中叫永久代

​ 在jdk8中叫元空間

​ 方法區在堆中,常量池存在方法區中。

​ jdk7版本中,字元串常量池位于堆中的永久代中,

​ 在JDK1.8 hotspot移除了永久代用元空間取而代之, 這時候字元串常量池還在堆, 運作時常量池還在方法區, 隻不過方法區的實作從永久代變成了元空間(堆外記憶體)

[GC (Allocation Failure) [PSYoungGen: 506K->488K(1024K)] 506K->512K(1536K), 0.0005064 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC (Ergonomics) [PSYoungGen: 1006K->222K(1024K)] [ParOldGen: 504K->426K(512K)] 1511K->648K(1536K), [Metaspace: 3291K->3291K(1056768K)], 0.0044686 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[外鍊圖檔轉存失敗,源

淺談Jvm虛拟機Jvm虛拟機筆記
淺談Jvm虛拟機Jvm虛拟機筆記

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Sdt29Q3G-1605170450434)(…/AppData/Roaming/Typora/typora-user-images/image-20201112132630174.png)]

淺談Jvm虛拟機Jvm虛拟機筆記

​ jvm一覽圖

2.GC:(四大回收算法)

  • 分區算法

原理

上面介紹的分代收集算法是将對象的生命周期按長短劃分為兩個部分, 而分區算法則将整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區間. 在相同條件下, 堆空間越大, 一次GC耗時就越長, 進而産生的停頓也越長. 為了更好地控制GC産生的停頓時間, 将一塊大的記憶體區域分割為多個小塊, 根據目标停頓時間, 每次合理地回收若幹個小區間(而不是整個堆), 進而減少一次GC所産生的停頓.

淺談Jvm虛拟機Jvm虛拟機筆記
  • GC分帶算法;
  • 次數上頻繁收集Young區
  • 次數上較少收集old區
  • 基本不動元空間
淺談Jvm虛拟機Jvm虛拟機筆記

JVM在進行GC時,并非每次都對上面三個記憶體區域- - 起回收的,大部分時候回收的都是指新生代。

是以GC按照回收的區域又分了兩種類型,一種是普通GC (minor GC),一種是全局GC (major GC or Full GC)

Minor GC利和Fu/l GC的差別

普通GC (minor GC) :隻針對新生代區域的GC,指發生在新生代的垃圾收集動作,因為大多數Java對象存活率都不高,是以Minor GC非常頻繁,- -般回收速度也比較快。

全局GC(majorGCorFullGC):指發生在老年代的垃圾收集動作,出現了MajorGC,經常會伴随至少–次的MinorGC(但

并不是絕對的)。Major GC的速度一般要比Minor GC慢上10倍以上

為什麼FullGC要比GC慢?

答:FullGC發生在old區 old區占堆空間的2/3.是以回收的速度慢于GC

HotSpot JVM把年輕代分為了三部分: 1 個Eden區和2個Survivor區(分别叫from和to)。預設比例為8:1:1,- -般情況下,新建立的

對象都會被配置設定到Eden區(- - 些大對象特殊處理),這些對象經過第一次Minor GC後,如果仍然存活,将會被移到Survivor區。對象在

Survivor區中每熬過-次Minor GC,年齡就會增加1歲,當它的年齡增加到一-定程度時,就會被移動到年老代中。因為年輕代中的對象

基本都是朝生夕死的(90%以上:), 是以在年輕代的垃圾回收算法使用的是複制算法,複制算法的基本思想就是将記憶體分為兩塊,每次隻

用其中-一塊,當這一-塊記憶體用完,就将還活着的對象複制到另外- - 塊上面。複制算法不會産生記憶體碎片。

所謂的複制算法===》就是講記憶體分為兩塊,一塊是eden+from 一塊是to區。

淺談Jvm虛拟機Jvm虛拟機筆記

複制算法它的缺點也是相當明顯的。

1、它浪費了一半的記憶體,這太要命了。

2、如果對象的存活率很高,我們可以極端一一點,假設是100%存活,那麼我們需要将所有對象都複制-遍,并将所有引用位址重

置一遍。複制這一.工作所花費的時間,在對象存活率達到一定程度時, 将會變的不可忽視。是以從以 上描述不難看出,複制算法要想使

用,最起碼對象的存活率要非常低才行,而且最重要的是,我們必須要克服50%記憶體的浪費。

2.1.引用計數法:

原理

引用計數算法很簡單,它實際上是通過在對象頭中配置設定一個空間來儲存該對象被引用的次數。如果該對象被其它對象引用,則它的引用計數加一,如果删除對該對象的引用,那麼它的引用計數就減一,當該對象的引用計數為0時,那麼該對象就會被回收。

淺談Jvm虛拟機Jvm虛拟機筆記
淺談Jvm虛拟機Jvm虛拟機筆記

引用計數垃圾收集機制,它隻是在引用計數變化為0時即刻發生,而且隻針對某一個對象以及它所依賴的其它對象。是以,我們一般也稱呼引用計數垃圾收集為直接的垃圾收集機制.垃圾收集的開銷被分攤到整個應用程式的運作當中了,而不是在進行垃圾收集時,要挂起整個應用的運作,直到對堆中所有對象的處理都結束。是以,采用引用計數的垃圾收集不屬于嚴格意義上的"Stop-The-World"的垃圾收集機制。

優點:

實時性較高, 不需要等到記憶體不夠時才回收

垃圾回收時不用挂起整個程式, 不影響程式正常運作.

缺點:

回收時不移動對象, 是以會造成記憶體碎片問題.

2.2标記清除法:(标垃圾 清除垃圾)

原理

它的做法是當堆中的有效記憶體空間被耗盡的時候,就會停止整個程式(也被成為stop the world),然後進行兩項工作,第一項則是标記,第二項則是清除。

标記:标記的過程其實就是,從根對象開始周遊所有的對象,然後将所有存活的對象标記為可達的對象。

清除:清除的過程将周遊堆中所有的對象,将沒有标記的對象全部清除掉。

優點 : 實作簡單

缺點 :

效率低, 因為标記和清除兩個動作都要周遊所有的對象

垃圾收集後有可能會造成大量的記憶體碎片, 垃圾回收時會造成應用程式暫停.

淺談Jvm虛拟機Jvm虛拟機筆記

用通俗的話解釋一下标記清除算法,就是當程式運作期間,若可以使用的記憶體被耗盡的時候,GC線程就會被觸發并将程式暫停,随後将要回收的對象标記–遍,最終統畫收這些對象,完成标記清理工作接下來便讓應用程式恢複運作。

2.3标記壓縮法:

原理

既然叫标記-壓縮算法,那麼它也分為兩個階段,一個是标記(mark),一個是壓縮(compact).

标記壓縮算法是在标記清除算法的基礎之上,做了優化改進的算法。和标記清除算法一樣,也是從根節點開始,對對象的引用進行标記,在清理階段,并不是簡單的清理未标記的對象,而是将存活的對象壓縮到記憶體的一端,然後清理邊界以外的垃圾,進而解決 了碎片化的問題。

标記 : 标記的過程其實就是,從根對象開始周遊所有的對象,然後将所有存活的對象标記為可達的對象。

壓縮 : 移動所有的可達對象到堆記憶體的同一個區域中,使他們緊湊的排列在一起,進而将所有非可達對象釋放出來的空閑記憶體都集中在一起,通過這樣的方式來達到減少記憶體碎片的目的。

優點 :

标記壓縮算法是對标記清除算法的優化, 解決了碎片化的問題

缺點 :

還是效率問題, 在标記清除算法上又多加了一步, 效率可想而知了

淺談Jvm虛拟機Jvm虛拟機筆記

2.4複制算法

原理

複制算法的核心就是,将原有的記憶體空間一分為二,每次隻用其中的一塊,在垃圾回收時,将正在使用的對象複制到另一個記憶體空間中,并以此排列, 然後将該記憶體空間清空,交換兩個記憶體的角色,完成垃圾的回收。

小結

優點:

在垃圾多的情況下(新生代), 效率較高

清理後, 記憶體無碎片

優點:

在垃圾多的情況下(新生代), 效率較高

清理後, 記憶體無碎片

所謂的複制算法===》就是講記憶體分為兩塊,一塊是eden+from 一塊是to區。

複制算法它的缺點也是相當明顯的。

1、它浪費了一半的記憶體,這太要命了。

2、如果對象的存活率很高,我們可以極端一一點,假設是100%存活,那麼我們需要将所有對象都複制-遍,并将所有引用位址重

置一遍。複制這一.工作所花費的時間,在對象存活率達到一定程度時, 将會變的不可忽視。是以從以 上描述不難看出,複制算法要想使

用,最起碼對象的存活率要非常低才行,而且最重要的是,我們必須要克服50%記憶體的浪費。

jvm