偶爾發現tomcat中有一個 hs_err_pid8706.log 的日志,網上查了一下,當jvm出現致命錯誤時會寫如該文件(但是期間也沒發生什么問題,估計是停止重啟的時候產生的),
其中有如下這樣一段gc相關記錄日志,其中有一段關於 Metaspace的,
Heap: PSYoungGen total 204288K, used 50381K [0x000000076d400000, 0x000000077af80000, 0x00000007c0000000) eden space 197632K, 22% used [0x000000076d400000,0x000000076feb59c0,0x0000000779500000) from space 6656K, 99% used [0x000000077a000000,0x000000077a67dc08,0x000000077a680000) to space 11264K, 0% used [0x0000000779500000,0x0000000779500000,0x000000077a000000) ParOldGen total 470016K, used 146509K [0x00000006c7c00000, 0x00000006e4700000, 0x000000076d400000) object space 470016K, 31% used [0x00000006c7c00000,0x00000006d0b13588,0x00000006e4700000) Metaspace used 87890K, capacity 94498K, committed 94720K, reserved 1134592K class space used 9454K, capacity 9950K, committed 9984K, reserved 1048576K
看上面的紅色部分,代表元數據的內存情況,但是不知道這個里面的 used ,capacity ,committed ,reserved 具體都代表什么意思。
java網站(https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/considerations.html )的解釋如下:
Class Metadata
Java classes have an internal representation within Java Hotspot VM and are referred to as class metadata. In previous releases of Java Hotspot VM, the class metadata was allocated in the so called permanent generation. In JDK 8, the permanent generation was removed and the class metadata is allocated in native memory. The amount of native memory that can be used for class metadata is by default unlimited. Use the option MaxMetaspaceSize
to put an upper limit on the amount of native memory used for class metadata.
Java Hotspot VM explicitly manages the space used for metadata. Space is requested from the OS and then divided into chunks. A class loader allocates space for metadata from its chunks (a chunk is bound to a specific class loader). When classes are unloaded for a class loader, its chunks are recycled for reuse or returned to the OS. Metadata uses space allocated by mmap
, not by malloc
.
If UseCompressedOops
is turned on and UseCompressedClassesPointers
is used, then two logically different areas of native memory are used for class metadata. UseCompressedClassPointers
uses a 32-bit offset to represent the class pointer in a 64-bit process as does UseCompressedOops
for Java object references. A region is allocated for these compressed class pointers (the 32-bit offsets). The size of the region can be set with CompressedClassSpaceSize
and is 1 gigabyte (GB) by default. The space for the compressed class pointers is reserved as space allocated by mmap
at initialization and committed as needed. The MaxMetaspaceSize
applies to the sum of the committed compressed class space and the space for the other class metadata.
Class metadata is deallocated when the corresponding Java class is unloaded. Java classes are unloaded as a result of garbage collection, and garbage collections may be induced in order to unload classes and deallocate class metadata. When the space committed for class metadata reaches a certain level (a high-water mark), a garbage collection is induced. After the garbage collection, the high-water mark may be raised or lowered depending on the amount of space freed from class metadata. The high-water mark would be raised so as not to induce another garbage collection too soon. The high-water mark is initially set to the value of the command-line option MetaspaceSize
. It is raised or lowered based on the options MaxMetaspaceFreeRatio
and MinMetaspaceFreeRatio
. If the committed space available for class metadata as a percentage of the total committed space for class metadata is greater than MaxMetaspaceFreeRatio
, then the high-water mark will be lowered. If it is less than MinMetaspaceFreeRatio
, then the high-water mark will be raised.
Specify a higher value for the option MetaspaceSize
to avoid early garbage collections induced for class metadata. The amount of class metadata allocated for an application is application-dependent and general guidelines do not exist for the selection of MetaspaceSize
. The default size of MetaspaceSize
is platform-dependent and ranges from 12 MB to about 20 MB.
Information about the space used for metadata is included in a printout of the heap. A typical output is shown in Example 11-1, "Typical Heap Printout".
Example 11-1 Typical Heap Printout
Heap PSYoungGen total 10752K, used 4419K [0xffffffff6ac00000, 0xffffffff6b800000, 0xffffffff6b800000) eden space 9216K, 47% used [0xffffffff6ac00000,0xffffffff6b050d68,0xffffffff6b500000) from space 1536K, 0% used [0xffffffff6b680000,0xffffffff6b680000,0xffffffff6b800000) to space 1536K, 0% used [0xffffffff6b500000,0xffffffff6b500000,0xffffffff6b680000) ParOldGen total 20480K, used 20011K [0xffffffff69800000, 0xffffffff6ac00000, 0xffffffff6ac00000) object space 20480K, 97% used [0xffffffff69800000,0xffffffff6ab8add8,0xffffffff6ac00000) Metaspace used 2425K, capacity 4498K, committed 4864K, reserved 1056768K class space used 262K, capacity 386K, committed 512K, reserved 1048576K
In the line beginning with Metaspace
, the used
value is the amount of space used for loaded classes. The capacity
value is the space available for metadata in currently allocated chunks. The committed
value is the amount of space available for chunks. The reserved
value is the amount of space reserved (but not necessarily committed) for metadata. The line beginning with class space
line contains the corresponding values for the metadata for compressed class pointers.
全英文,看不太明白,繼續搜索了一下,發現有這樣一篇博客,https://stackoverflow.com/questions/40891433/understanding-metaspace-line-in-jvm-heap-printout
還配了一張圖,如下。
翻譯了一下,大概理解為:
used:加載的類的空間量。
capacity: 當前分配塊的元數據的空間。
committed: 空間塊的數量。
reserved:元數據的空間保留(但不一定提交)的量(提交,什么意思?)。
從操作系統中請求的空間被分成多個塊,一個類加載器為其元數據分配元數據的空間(塊被綁定到特定的類加載器)
Metaspace是從JVM進程的虛擬地址空間中分離出來的。JVM在啟動時根據-XX:MetaspaceSize保留初始大小,該大小具有特定於平台的默認值。
[root@localhost ~]# java -XX:+PrintFlagsInitial|grep Meta uintx InitialBootClassLoaderMetaspaceSize = 4194304 {product} uintx MaxMetaspaceExpansion = 5452592 {product} uintx MaxMetaspaceFreeRatio = 70 {product} uintx MaxMetaspaceSize = 18446744073709551615 {product} uintx MetaspaceSize = 21810376 {pd product} uintx MinMetaspaceExpansion = 340784 {product} uintx MinMetaspaceFreeRatio = 40 {product} bool TraceMetadataHumongousAllocation = false {product} bool UseLargePagesInMetaspace = false {product}
reserved 指的是元空間的總大小,空間被分成塊,每個塊只能包含與某一個類加載器關聯的類元數據。
Metaspace由一個或多個虛擬空間組成。虛擬空間是由操作系統獲得的連續地址空間。他們是按需分配的。
在分配時,虛擬空間預留(reserves)了操作系統的內存,但還沒有提交。Metaspace reserved是所有虛擬空間的總大小。
虛擬空間中的分配單元是Metachunk 、當從虛擬空間分配新塊時、相應的內存將committed, Metaspace committed是所有塊的總大小。
塊的大小可能不同,當類加載器被垃圾收集時, 所有屬於它的Metachunks都將被釋放。
免費的塊在全局免費列表中維護,Metaspace capacity是所有分配(即非空閑塊)塊的總大小。
新塊分配過程:
1:查找空閑列表中的有沒有空閑塊。
2:如果沒有合適的空閑塊,就從當前虛擬空間中分配一個新的塊。
3: 如果當前虛擬空間已經耗盡,則預留(reserves)一個新的虛擬空間。
類元數據在一個塊中分配,塊不包含來自多個類加載器的數據,但是一個類加載器可能有幾個塊。Metaspace used是所有塊的所有類元數據的總大小。
Metaspace空間的優化: https://blog.csdn.net/bolg_hero/article/details/78189621
通過以下的幾個參數對Metaspace進行控制
-XX:MetaspaceSize=N
這個參數是初始化的Metaspace大小,該值越大觸發Metaspace GC的時機就越晚。隨着GC的到來,虛擬機會根據實際情況調控Metaspace的大小,可能增加上線也可能降低。在默認情況下,這個值大小根據不同的平台在12M到20M浮動。使用java -XX:+PrintFlagsInitial命令查看本機的初始化參數,-XX:Metaspacesize為21810376B(大約20.8M)。
-XX:MaxMetaspaceSize=N
這個參數用於限制Metaspace增長的上限,防止因為某些情況導致Metaspace無限的使用本地內存,影響到其他程序。在本機上該參數的默認值為4294967295B(大約4096MB)。
-XX:MinMetaspaceFreeRatio=N
當進行過Metaspace GC之后,會計算當前Metaspace的空閑空間比,如果空閑比小於這個參數,那么虛擬機將增長Metaspace的大小。在本機該參數的默認值為40,也就是40%。設置該參數可以控制Metaspace的增長的速度,太小的值會導致Metaspace增長的緩慢,Metaspace的使用逐漸趨於飽和,可能會影響之后類的加載。而太大的值會導致Metaspace增長的過快,浪費內存。
-XX:MaxMetasaceFreeRatio=N
當進行過Metaspace GC之后, 會計算當前Metaspace的空閑空間比,如果空閑比大於這個參數,那么虛擬機會釋放Metaspace的部分空間。在本機該參數的默認值為70,也就是70%。
-XX:MaxMetaspaceExpansion=N
Metaspace增長時的最大幅度。在本機上該參數的默認值為5452592B(大約為5MB)。
-XX:MinMetaspaceExpansion=N
Metaspace增長時的最小幅度。在本機上該參數的默認值為340784B(大約330KB為)。