一些看過Java8以后的垃圾回收日志的同學一般會對這么一句話感到很困惑:
Metaspace used 2425K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 262K, capacity 386K, committed 512K, reserved 1048576K
第一個問題是,按照一般的理解,metaspace似乎是一個整體,怎么還分成了四個部分?
第二個問題是,怎么metaspace之后,還有一個class space?兩者之間的關系是怎樣的?
先來看第一個問題。在Oracle文檔(https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/considerations.html)里面的最后一段有對這個問題的簡單解釋:
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.
這個解釋并不怎么樣。
先來看一幅圖:

首先可以看到的是,這些used,capacity,committed和reserved并不純粹是JVM的概念,它和操作系統(tǒng)相關。
先來看committed和reserved。reserved是指,操作系統(tǒng)已經(jīng)為該進程“保留”的。所謂的保留,更加接近一種記賬的概念,就是操作系統(tǒng)承諾說一大塊連續(xù)的內存已經(jīng)是你這個進程的了。注意的是,這里強調的是連續(xù)的內存,并且強調的是一種名義歸屬。那么實際上這一大塊內存有沒有真實對應的物理內存呢?答案是不知道。
那么什么時候才知道呢?等進程committed的時候。當進程真的要用這個連續(xù)地址空間的時候,操作系統(tǒng)才會分配真正的內存。所以,這也就是意味著,這個過程會失敗。
舉個例子來說,就好比楊白勞找黃世仁借糧,楊白勞說借我一百斤,黃世仁在本子上記錄了一筆說可以,借給你了,但是現(xiàn)在不能給你,等你要吃的時候再來領。于是楊白勞過了一個星期,領了三十斤,這三十斤就是committed的,之前那一百斤就是reserved的。很顯然,如果黃世仁給太多人都記了一筆,說不定等楊白勞去借的時候,就沒有了。
因此我才說,這個reserved更加接近記賬的概念。
used和capacity就是JVM的概念了。這兩個概念非常接近JVM一些集合框架的概念。一些Java集合框架,比如某種List的實現(xiàn),會有size和capacity的概念。比如說ArrayList的實現(xiàn)里面就有capacity和size的概念。假如說我創(chuàng)建了一個可以存放20個元素的ArrayList,但是我實際上只放了10個元素,那么capacity就是20,而size就是10.這里的size和used就是一個概念。那么“元素”則是一個個內存塊"block“。
capacity和committed的關系也可以此類比,只不過capacity反而對應到used,committed對應到capacity,而所謂的”元素“,就是chunk。
至于class space,要記住的是,metaspace并不是全部用來放類對象的。比如說,因為每一個ClassLoader都被分配了一塊內存,這塊內存可能并沒有被用完,于是就會有一些內存碎片;metaspace還需要放所謂靜態(tài)變量。所以,class space是指實際上被用于放class的那塊內存的和。
以后如果有人面試面你metaspace,把這一段吹出來,差不多可以秒掉90%的面試官了。畢竟大部分面試官也是小菜雞,只不過他占了先手而已。