Java 内存分区之什么是 CCS区 Compressed Class Space 类压缩空间

https://blog.csdn.net/qq_27093465/article/details/106760961

 

Java 内存分区之什么是 CCS区 Compressed Class Space 类压缩空间  

了解到什么是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,那么对象指针压缩和类指针压缩都会被关闭。

再多的消化不了拉。

参考:

深入理解堆外内存 Metaspace 

_________________________________________________________________________________________________________

 

 
链接:https://www.zhihu.com/question/268392125/answer/1271619241 

JVM 有个功能是 CompressedOops ,目的是为了在 64bit 机器上使用 32bit 的原始对象指针(oop,ordinary object pointer,这里直接就当成指针概念理解就可以了,

不用关心啥是 ordinary)来节约成本(减少内存/带宽使用),提高性能(提高 Cache 命中率)。使用了这个压缩功能,每个对象中的 Klass* 字段就会被压缩成 32bit(不是所有的 oop 都会被压缩的),

总所周知 Klass* 指向的 Klass 在永久代(Java7 及之前)。但是在 Java8 及之后,永久代没了,有了一个 Metaspace,于是之前压缩指针 Klass* 指向的这块 Klass 区域有了一个名字 —— Compressed Class Space。

Compressed Class Space 是 Metaspace 的一部分,默认大小为 1G。所以其实 Compressed Class Space 这个名字取得很误导,压缩的并不是 Klass,而是 Klass*。

 

JVM 也为 compressed class pointers(Klass*)多了俩选项:

  • -XX:+UseCompressedClassPointers(压缩开关)
  • -XX:CompressedClassSpaceSize(Compressed Class Space 空间大小限制)。

-XX:+UseCompressedClassPointers 是需要 -XX:+UseCompressedOops 开启的,所以堆大小要是大于 32G,CompressedOops 自动关闭,CompressedClassPointers 也会关闭的,

关闭了就没有 Compressed Class Space 了,这块就是 Class Space 了。

-XX:CompressedClassSpaceSize 大小的设置也是有限制,因为压缩开关是受制于 32G,所以这个自然也是不能大于 32G,不过 hotspot 规定了这个参数不准大于 3G,

所以这个参数其实是不能大于 3G。

一般来说,平均一个 Klass 大小可以当成 1K 来算,默认的 1G 大小可以存储 100 万的 Klass。如果遇到了 `java.lang.OutOfMemoryError: Compressed class space`,

就是类太多了,需要结合具体情况去选择 JVM 调优还是 bug 排查。

 

posted @ 2022-11-15 13:43  kelelipeng  阅读(553)  评论(0编辑  收藏  举报