一些看过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的概念,它和操作系统相关。
先来看committed
和reserved
。reserved
是指,操作系统已经为该进程“保留”的。所谓的保留,更加接近一种记账的概念,就是操作系统承诺说一大块连续的内存已经是你这个进程的了。注意的是,这里强调的是连续的内存,并且强调的是一种名义归属。那么实际上这一大块内存有没有真实对应的物理内存呢?答案是不知道。
那么什么时候才知道呢?等进程committed
的时候。当进程真的要用这个连续地址空间的时候,操作系统才会分配真正的内存。所以,这也就是意味着,这个过程会失败。
举个例子来说,就好比杨白劳找黄世仁借粮,杨白劳说借我一百斤,黄世仁在本子上记录了一笔说可以,借给你了,但是现在不能给你,等你要吃的时候再来领。于是杨白劳过了一个星期,领了三十斤,这三十斤就是committed
的,之前那一百斤就是reserved
的。很显然,如果黄世仁给太多人都记了一笔,说不定等杨白劳去借的时候,就没有了。
因此我才说,这个reserved
更加接近记账的概念。
used
和capacity
就是JVM的概念了。这两个概念非常接近JVM一些集合框架的概念。一些Java集合框架,比如某种List的实现,会有size
和capacity
的概念。比如说ArrayList
的实现里面就有capacity
和size
的概念。假如说我创建了一个可以存放20个元素的ArrayList
,但是我实际上只放了10个元素,那么capacity
就是20,而size
就是10.这里的size
和used
就是一个概念。那么“元素”则是一个个内存块"block“。
capacity
和committed
的关系也可以此类比,只不过capacity
反而对应到used
,committed
对应到capacity
,而所谓的”元素“,就是chunk
。
至于class space
,要记住的是,metaspace
并不是全部用来放类对象的。比如说,因为每一个ClassLoader
都被分配了一块内存,这块内存可能并没有被用完,于是就会有一些内存碎片;metaspace
还需要放所谓静态变量。所以,class space
是指实际上被用于放class
的那块内存的和。
以后如果有人面试面你metaspace
,把这一段吹出来,差不多可以秒掉90%的面试官了。毕竟大部分面试官也是小菜鸡,只不过他占了先手而已。
网友评论