了解到什么是ccs区,一般都是实际执行了jstat -gc 之后,看Java堆的gc相关的几个分区的gc信息,前面的s0,s1,e区,o区,还好猜,研究过分区的,不难猜出来这个分区是啥意思,M区虽然不知道是Metaspace元空间,但是错把这个M区当成Method area 方法区,也说的过去。这个ccsc就不好说了。之前的看的文章都没人说这个区是啥。
Java之jstat的用法:Java虚拟机 统计信息查看 工具
不了解这几个简写单词是啥意思的,可以参考一下之前的这个jstat的文章,里面对每一列的title的简写都做了解释。
什么是 Compressed Class Space
在 64 位平台上,HotSpot 使用了两个压缩优化技术,Compressed Object Pointers (“CompressedOops”) 和 Compressed Class Pointers。
压缩指针,指的是在 64 位的机器上,使用 32 位的指针来访问数据(堆中的对象或 Metaspace 中的元数据)的一种方式。
这样有很多的好处,比如 32 位的指针占用更小的内存,可以更好地使用缓存,在有些平台,还可以使用到更多的寄存器。
当然,在 64 位的机器中,最终还是需要一个 64 位的地址来访问数据的,所以这个 32 位的值是相对于一个基准地址的值。
下面将描述 Compressed Class Pointers:
每个 Java 对象,在它的头部,有一个引用指向 Metaspace 中的 Klass 结构。
当使用了 compressed class pointers,这个引用是 32 位的值,为了找到真正的 64 位地址,需要加上一个 base 值:
上面的内容应该很好理解,这项技术对 Klass 的分配带来的问题是:由于 32 位地址只能访问到 4G 的空间,所以最大只允许 4G 的 Klass 地址。这项限制也意味着,JVM 需要向 Metaspace 分配一个连续的地址空间。
当从系统申请内存时,通过调用系统接口 malloc(3) 或 mmap(3),操作系统可能返回任意一个地址值,所以在 64位系统中,它并不能保证在 4G 的范围内。
所以,我们只能用一个 mmap() 来申请一个区域单独用来存放 Klass 对象。我们需要提前知道这个区域的大小,而且不能超过 4G。显然,这种方式是不能扩展的,因为这个地址后面的内存可能是被占用的。
只有 Klass 结构有这个限制,对于其他的 class metadata 没有这个必要: 因为只有 Klass 实例是通过 Java 对象 header 中的压缩指针访问的。其他的 metadata 都是通过 64 位的地址进行访问的,所以它们可以被放到任意的地址上。
所以,我们决定将 Metaspace 分为两个区域:non-class part 和 class part。
- class part:存放 Klass 对象,需要一个连续的不超过 4G 的内存
- non-class part:包含其他的所有 metadata
class part 被称作 Compressed Class Space,这个名字会有点怪,因为 Klass 本身其实没有使用压缩技术,而是引用它们的指针被压缩了。
compressed class space 空间的大小,是通过 -XX:CompressedClassSpaceSize 指定的。
我们需要提前知道自己需要多少内存,它的默认值是 1G。当然这个 1G 并不是真的使用了操作系统的 1G,而是虚拟地址映射。
开关: UseCompressedClassPointers, UseCompressedOops
-XX:+UseCompressedOops
允许对象指针压缩。
-XX:+UseCompressedClassPointers
允许类指针压缩。
它们默认都是开启的,可以手动关闭它们。
如果不允许类指针压缩,那么将没有 compressed class space 这个空间,并且-XX:CompressedClassSpaceSize
这个参数无效。
-XX:-UseCompressedClassPointers
需要搭配 -XX:+UseCompressedOops
,但是反过来不是: 我们可以只压缩对象指针,不压缩类指针。
这里面为什么这么规定我也不懂,但是从直觉上来说,压缩对象指针显然是比较重要的,能获得较大的收益。也许就是基于这种考量吧:你连对象指针都不压缩,类指针压缩不压缩又有什么关系呢?
注意,对象指针压缩要求堆小于 32G,所以如果堆大于等于 32G,那么对象指针压缩和类指针压缩都会被关闭。
再多的消化不了拉。
参考: