根據JVM內存區域的劃分,簡單的畫了下方的這個示意圖。區域主要分爲兩大塊,一塊是堆區(Heap),咱們所New出的對象都會在堆區進行分配,在C語言中的malloc所分配的方法就是從Heap區獲取的。而垃圾回收器主要是對堆區的內存進行回收的。html
而另外一部分則是非堆區,非堆區主要包括用於編譯和保存本地代碼的「代碼緩存區(Code Cache)」、保存JVM本身的靜態數據的「永生代(Perm Gen)」、存放方法參數局部變量等引用以及記錄方法調用順序的「Java虛擬機棧(JVM Stack)」和「本地方法棧(Local Method Stack)」。java
垃圾回收器主要回收的是堆區中未使用的內存區域,並對相應的區域進行整理。在堆區中,又根據對象內存的存活時間或者對象大小,分爲「年輕代」和「年老代」。「年輕代」中的對象是不穩定的易產生垃圾,而「年老代」中的對象比較穩定,不易產生垃圾。之因此將其分開,是分而治之,根據不一樣區域的內存塊的特色,採起不一樣的內存回收算法,從而提升堆區的垃圾回收的效率。web
永久代存放JVM運行時使用的類。永久代一樣包含了Java SE庫的類和方法。永久代的對象在full GC時進行垃圾收集。算法
PermGen space的全稱是Permanent Generation space,是指內存的永久保存區域,說說爲何會內存益出:這一部分用於存放Class和Meta的信息,Class在被 Load的時候被放入PermGen space區域,它和和存放Instance的Heap區域不一樣,因此若是你的APP會LOAD不少CLASS的話,就極可能出現PermGen space錯誤。這種錯誤常見在web服務器對JSP進行pre compile的時候。json
JVM 種類有不少,好比 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM(淘寶好樣的!)等等。固然武林盟主是Hotspot了,這個毫無爭議。須要注意的是,PermGen space是Oracle-Sun Hotspot纔有,JRockit以及J9是沒有這個區域。bootstrap
持久代中包含了虛擬機中全部可經過反射獲取到的數據,好比Class和Method對象。不一樣的Java虛擬機之間可能會進行類共享,所以持久代又分爲只讀區和讀寫區。數組
JVM用於描述應用程序中用到的類和方法的元數據也存儲在持久代中。JVM運行時會用到多少持久代的空間取決於應用程序用到了多少類。除此以外,Java SE庫中的類和方法也都存儲在這裏。緩存
若是JVM發現有的類已經再也不須要了,它會去回收(卸載)這些類,將它們的空間釋放出來給其它類使用。Full GC會進行持久代的回收。服務器
持久代的大小數據結構
根據上面的各類緣由,永久代最終被移除,方法區移至Metaspace,字符串常量移至Java Heap。
這部份內存空間將所有移除。
JVM的參數:PermSize 和 MaxPermSize 會被忽略並給出警告(若是在啓用時設置了這兩個參數)。
隨着JDK8的到來,JVM再也不有PermGen。但類的元數據信息(metadata)還在,只不過再也不是存儲在連續的堆空間上,而是移動到叫作「Metaspace」的本地內存(Native memory)中。
Klass Metaspace和NoKlass Mestaspace都是全部classloader共享的,因此類加載器們要分配內存,可是每一個類加載器都有一個SpaceManager,來管理屬於這個類加載的內存小塊。若是Klass Metaspace用完了,那就會OOM了,不過通常狀況下不會,NoKlass Mestaspace是由一塊塊內存慢慢組合起來的,在沒有達到限制條件的狀況下,會不斷加長這條鏈,讓它能夠持續工做。
元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。所以,默認狀況下,元空間的大小僅受本地內存限制,但能夠經過如下參數來指定元空間的大小:
-XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:若是釋放了大量的空間,就適當下降該值;若是釋放了不多的空間,那麼在不超過MaxMetaspaceSize時,適當提升該值。
-XX:MaxMetaspaceSize,最大空間,默認是沒有限制的。
除了上面兩個指定大小的選項之外,還有兩個與 GC 相關的屬性:
-XX:MinMetaspaceFreeRatio,在GC以後,最小的Metaspace剩餘空間容量的百分比,減小爲分配空間所致使的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC以後,最大的Metaspace剩餘空間容量的百分比,減小爲釋放空間所致使的垃圾收集
-verbose參數是爲了獲取類型加載和卸載的信息
咱們來看下JVM是如何給元數據分配虛擬內存的空間的
你能夠看到虛擬內存空間是如何分配的(vs1,vs2,vs3) ,以及類加載器的內存塊是如何分配的。CL是Class Loader的縮寫。
要想理解下面這張圖,你得搞清楚這些指針都是什麼東西。
JVM中,每一個對象都有一個指向它自身類的指針,不過這個指針只是指向具體的實現類,而不是接口或者抽象類。
對於32位的JVM:
_mark : 4字節常量
_klass: 指向類的4字節指針 對象的內存佈局中的第二個字段( _klass,在32位JVM中,相對對象在內存中的位置的偏移量是4,64位的是8)指向的是內存中對象的類定義。
64位的JVM:
_mark : 8字節常量
_klass: 指向類的8字節的指針
開啓了指針壓縮的64位JVM: _mark : 8字節常量
_klass: 指向類的4字節的指針
類指針壓縮空間(Compressed Class Pointer Space)
只有是64位平臺上啓用了類指針壓縮纔會存在這個區域。對於64位平臺,爲了壓縮JVM對象中的_klass指針的大小,引入了類指針壓縮空間(Compressed Class Pointer Space)。
默認是開啓
),則UseCompressedOops會使用32-bit的offset來表明java object的引用,而UseCompressedClassPointers則使用32-bit的offset來表明64-bit進程中的class pointer;可使用CompressedClassSpaceSize來設置這塊的空間大小查看CompressedClassSpace大小,見《12、jdk工具之jcmd介紹(堆轉儲、堆分析、獲取系統信息、查看堆外內存)》
壓縮指針後的內存佈局
指針壓縮概要
使用-XX:+UseCompressedOops壓縮對象指針 "oops"指的是普通對象指針("ordinary" object pointers)。 Java堆中對象指針會被壓縮成32位。 使用堆基地址(若是堆在低26G內存中的話,基地址爲0)
使用-XX:+UseCompressedClassPointers選項來壓縮類指針
對象中指向類元數據的指針會被壓縮成32位
類指針壓縮空間會有一個基地址
類指針壓縮空間只包含類的元數據,好比InstanceKlass, ArrayKlass 僅當打開了UseCompressedClassPointers選項才生效 爲了提升性能,Java中的虛方法表也存放到這裏 這裏到底存放哪些元數據的類型,目前仍在減小
元空間包含類的其它比較大的元數據,好比方法,字節碼,常量池等。
元空間的內存管理由元空間虛擬機來完成。先前,對於類的元數據咱們須要不一樣的垃圾回收器進行處理,如今只須要執行元空間虛擬機的C++代碼便可完成。在元空間中,類和其元數據的生命週期和其對應的類加載器是相同的。話句話說,只要類加載器存活,其加載的類的元數據也是存活的,於是不會被回收掉。
準確的來講,每個類加載器的存儲區域都稱做一個元空間,全部的元空間合在一塊兒就是咱們一直說的元空間。當一個類加載器被垃圾回收器標記爲再也不存活,其對應的元空間會被回收。在元空間的回收過程當中沒有重定位和壓縮等操做。可是元空間內的元數據會進行掃描來肯定Java引用。
元空間虛擬機負責元空間的分配,其採用的形式爲組塊分配。組塊的大小因類加載器的類型而異。在元空間虛擬機中存在一個全局的空閒組塊列表。當一個類加載器須要組塊時,它就會從這個全局的組塊列表中獲取並維持一個本身的組塊列表。當一個類加載器再也不存活,那麼其持有的組塊將會被釋放,並返回給全局組塊列表。類加載器持有的組塊又會被分紅多個塊,每個塊存儲一個單元的元信息。組塊中的塊是線性分配(指針碰撞分配形式)。組塊分配自內存映射區域。這些全局的虛擬內存映射區域以鏈表形式鏈接,一旦某個虛擬內存映射區域清空,這部份內存就會返回給操做系統。
上圖展現的是虛擬內存映射區域如何進行元組塊的分配。類加載器1和3代表使用了反射或者爲匿名類加載器,他們使用了特定大小組塊。 而類加載器2和4根據其內部條目的數量使用小型或者中型的組塊。
使用-XX:MaxMetaspaceSize參數能夠設置元空間的最大值,默認是沒有上限的,也就是說你的系統內存上限是多少它就是多少。-XX:MetaspaceSize選項指定的是元空間的初始大小,若是沒有指定的話,元空間會根據應用程序運行時的須要動態地調整大小。
正如前面提到了,Metaspace VM管理Metaspace空間的增加。但有時你會想經過在命令行顯示的設置參數-XX:MaxMetaspaceSize來限制Metaspace空間的增加。默認狀況下,-XX:MaxMetaspaceSize並無限制,所以,在技術上,Metaspace的尺寸能夠增加到交換空間,而你的本地內存分配將會失敗。
每次垃圾收集以後,Metaspace VM會自動的調整high watermark,推遲下一次對Metaspace的垃圾收集。
這兩個參數,-XX:MinMetaspaceFreeRatio和-XX:MaxMetaspaceFreeRatio,相似於GC的FreeRatio參數,能夠放在命令行。
針對Metaspace,JDK自帶的一些工具作了修改來展現Metaspace的信息:
示例1:jmap -clstats
[ciadmin@2-103test_app pos-gateway-cloud]$ jmap -clstats 26964 Attaching to process ID 26964, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.131-b11 finding class loader instances ..done. computing per loader stat ..done. please wait.. computing liveness..................................................................................liveness analysis may be inaccurate ... class_loader classes bytes parent_loader alive? type <bootstrap> 2699 4611703 null live <internal> 0x00000000a1013a00 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a3e931e8 1 880 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d280 1 1471 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1c057c8 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1013938 1 1474 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1013d38 1 1471 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a141ae78 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d1b8 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a163c658 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a293afa8 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a19ec0a0 15 70893 0x00000000a001b938 live com/aliyun/openservices/shade/com/alibaba/fastjson/util/ASMClassLoader@0x000000010066b7a0 0x00000000a2778848 1 1474 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a141a900 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d8c0 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a163c720 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 ... 0x00000000a094fe68 0 0 0x00000000a0007438 live java/net/URLClassLoader@0x000000010000ecd0 total = 177 12836 20539140 N/A alive=9, dead=168 N/A [ciadmin@2-103test_app pos-gateway-cloud]$
示例二:jstat -gc 26964
[ciadmin@2-103test_app pos-gateway-cloud]$ jstat -gc 26964 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 3072.0 3072.0 2384.8 0.0 62976.0 6699.1 445440.0 67911.5 69760.0 68124.8 8320.0 7929.0 3792 36.649 12 1.971 38.620 [ciadmin@2-103test_app pos-gateway-cloud]$
示例三:jcmd 5943 GC.class_stats
[ciadmin@2-103test_app pos-gateway-cloud]$ jcmd 5943 GC.class_stats 5943: GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions [ciadmin@2-103test_app pos-gateway-cloud]$
說是:應用程序啓動時增長-XX:+UnlockDiagnosticVMOptions參數
加了上面的參數後,從新來一把以下:
D:\workspace\study\target\classes\com\dxz\jvm>jcmd 4332 GC.class_stats 4332: Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName 1 -1 258458040 480 0 0 0 0 0 24 584 608 [Ljava.lang.Object; 2 368 217343856 1000 0 6864 51 3955 13744 8664 13888 22552 java.util.HashMap 3 368 144895744 1432 0 15584 93 9536 37136 19104 37048 56152 java.util.concurrent.ConcurrentHashMap 4 363 99615560 928 0 8232 24 1719 6040 4392 11304 15696 java.net.URLClassLoader 5 -1 90560256 480 0 0 0 0 0 32 584 616 [Ljava.util.concurrent.ConcurrentHashMap$Node; 6 -1 90559840 480 0 0 0 0 0 32 584 616 [Ljava.util.WeakHashMap$Entry; 7 367 72447872 1384 0 5288 59 2245 13544 7080 14016 21096 java.util.Vector 8 367 54335928 1320 0 4936 49 2359 12104 6600 12592 19192 java.util.ArrayList 9 368 54335904 976 0 4952 32 1845 11968 4856 13744 18600 java.util.WeakHashMap 10 14 54335856 656 0 7488 33 1513 8504 4792 12496 17288 sun.misc.URLClassPath 11 14 45280240 504 0 4960 27 2551 11792 5232 12536 17768 java.security.AccessControlContext 12 14 45279920 528 0 4328 12 1024 3760 2488 6496 8984 java.security.ProtectionDomain 13 14 36226208 568 0 1344 8 223 1744 1024 2952 3976 java.util.concurrent.ConcurrentHashMap$Node 14 -1 36224752 496 0 1144 14 109 2520 1112 3272 4384 java.lang.Object 15 14 36224032 552 0 1840 7 410 2744 1288 4160 5448 java.lang.ref.ReferenceQueue 16 14 36223936 552 0 5320 14 1796 4648 3552 7328 10880 java.security.CodeSource 17 7 36223936 1424 0 864 6 88 1664 704 3552 4256 java.util.Stack 18 373 27167952 1008 0 808 4 69 1000 592 2528 3120 java.util.Collections$SynchronizedSet 19 -1 27167880 480 0 0 0 0 0 24 584 608 [Ljava.security.ProtectionDomain; 20 14 18112048 496 0 360 2 10 920 216 1720 1936 java.lang.ref.ReferenceQueue$Lock 21 -1 18111968 480 0 0 0 0 0 24 584 608 [Ljava.security.Principal; ... 484 14 0 496 0 1416 20 737 3736 2240 3680 5920 sun.util.locale.LocaleUtils 1535868936 298000 1536 955600 6934 264621 1445736 885528 1980368 2865896 Total 53591.2% 10.4% 0.1% 33.3% - 9.2% 50.4% 30.9% 69.1% 100.0% Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName D:\workspace\study\target\classes\com\dxz\jvm>
若是你理解了元空間的概念,很容易發現GC的性能獲得了提高。
java8中metaspace總結以下:
這部份內存空間將所有移除。
JVM的參數:PermSize 和 MaxPermSize 會被忽略並給出警告(若是在啓用時設置了這兩個參數)。
大部分類元數據都在本地內存中分配。
用於描述類元數據的「klasses」已經被移除。
默認狀況下,類元數據只受可用的本地內存限制(容量取決因而32位或是64位操做系統的可用虛擬內存大小)。
新參數(MaxMetaspaceSize)用於限制本地內存分配給類元數據的大小。若是沒有指定這個參數,元空間會在運行時根據須要動態調整。
對於僵死的類及類加載器的垃圾回收將在元數據使用達到「MaxMetaspaceSize」參數的設定值時進行。
適時地監控和調整元空間對於減少垃圾回收頻率和減小延時是頗有必要的。持續的元空間垃圾回收說明,可能存在類、類加載器致使的內存泄漏或是大小設置不合適。
前面已經提到,元空間虛擬機採用了組塊分配的形式,同時區塊的大小由類加載器類型決定。類信息並非固定大小,所以有可能分配的空閒區塊和類須要的區塊大小不一樣,這種狀況下可能致使碎片存在。元空間虛擬機目前並不支持壓縮操做,因此碎片化是目前最大的問題。