4年前 (2020-09-14)  爪哇岛 |   抢沙发  921 
文章评分 0 次,平均分 0.0

在本系列的前一篇文章中,元空间体系结构故意省略了压缩的类空间。这一点使情况更加复杂。

在64位平台上,hotspot使用称为压缩对象指针(“CompressedOops”)和压缩类指针的优化技术。两者都是同一事物的变体。

压缩指针是一种引用数据(Java堆中的对象或元空间中的类元数据)的方法,即使在64位平台上也使用32位引用。

这有许多优点,例如指针大小更小,从而减少内存占用和更好地利用缓存,并且在某些平台上可以使用更多的寄存器。

Note: A good explanation of Compressed Object Pointers can be found here: JVM Anatomy Quark #23: Compressed References.

Also, a similar motivation drives the Linux x32 abi.

因为最终一个人需要一个64位的地址来访问那个东西,那个32位的“指针”实际上是一个偏移量——可能是位移位——进入一个具有已知公共基的区域。

关于Metaspace,我们不关心压缩的oop,但必须处理压缩类指针:

每个Java对象的头中都有一个对Metaspace中Java堆之外的本机结构的引用:Class结构。

什么是metaspace的压缩类空间?

使用压缩类指针时,该引用是32位值。为了找到该结构的真正64位地址,我们向其添加一个已知的公共基,并可能将值左移三位:

什么是metaspace的压缩类空间?

该技术对如何分配这些Klass结构设置了技术限制:

Klass结构的每个可能的位置必须在4G(非移位模式)| 32G(移位模式)的范围内,以从公共基址1的32位偏移量可到达。

这两个限制意味着我们需要将元空间分配为一个连续的区域。

当通过malloc(3)或mmap(3)这样的系统API从系统分配内存时,地址由系统选择,并且可以是适合类型范围的任何值。因此,在64位平台上,当然不能保证后续分配会在范围限制内产生地址。E、 g.一个mmap(3)调用可以映射到0x0000000700000000,一个映射到0x0000000f0000000。

因此,我们必须使用一个mmap()调用来建立Klass对象的区域。因此,我们需要预先知道这个区域的大小,它不能大于32G,也永远不能可靠地扩展,因为超出其末端的地址范围可能已经被占用。

这些限制是严厉的。它们也只是真正需要用于Klass结构,而不是用于其他类元数据:目前只有Klass实例被压缩引用处理。因此,可以将其他64位指针放在任何位置。

因此决定将元空间分成两部分:“非类部分”和“类部分”:

  • 等级部分,包括Class结构,必须分配为一个不大于32G的连续区域。
  • 包含其他所有内容的非类部分则没有。

Terminology: The class part is called “Compressed Class Space” even though that is a bit of a misnomer since the Klass structures themselves are not compressed but the pointers to them.

压缩类空间的大小由-XX:CompressedClassSpaceSize决定。因为我们需要预先知道类空间的大小,所以该参数不能为空。如果省略,则默认为1GB。

更令人困惑的是,hotspot人为地将classspacesize压缩到3G的最大值——我真的不知道为什么。因此,除了32G的技术限制之外,我们还人为地设置了3G的限制。

另外请注意,我们一直在谈论虚拟尺寸,而不是漫画尺寸。这种记忆只有在需要的时候才会提交。非常简化,虚拟大小在大多数现代操作系统上几乎不需要任何成本,它只是一个addres空间预留。

由于Klass结构的平均大小为1K,一个默认大小为1G的压缩类空间将能够容纳大约一百万个Klass结构(参见调整元空间大小)。这是我们可以加载的类数量的唯一实际限制。

还请注意,当我们不使用CompressedOops运行时,compressedClasspointer将被禁用。如果我们通过-XX:-CompressedOops手动关闭CompressedOops,或者Java堆大于或等于32G,就会发生这种情况。

往期参考:https://javakk.com/160.html

Implementation

为了重用现有的元空间实现(请参见元空间体系结构),采用了一个技巧:

全局结构VirtualSpaceList和ChunkManager都是重复的,现在存在于两个变体中,“类空间”变量和“非类空间”变量。

但是由于类空间需要一个连续的地址范围,我们不能真正使用映射区域链;因此类空间列表退化了:它只包含一个节点,不能增长。与非类列表中的同类节点相比,这个节点是巨大的。这个节点就是压缩的类空间。

什么是metaspace的压缩类空间?

什么是metaspace的压缩类空间?

ClassLoaderMetaspace——每个类装入器结构都包含使用这个类装入器的块——现在需要两个链接的块列表,一个用于保存非类块,另一个用于类块。这也意味着我们将当前节点的“空闲”部分加倍,因为现在我们有两个节点。

开关:UseCompressedClassPointers、UseCompressedOops

-XX:+UseCompressedOops启用压缩对象指针

-XX:+UseCompressedClassPointers启用压缩类指针。

默认情况下,两者都处于打开状态,但可以手动关闭。

如果压缩类指针被关闭,我们将没有压缩的类空间,并且-XX:CompressedClassSpaceSize开关将被忽略。

-XX:+UseCompressedClassPointers需要-XX:+useCompressedDoops,但反之亦然:可以在没有压缩类指针的情况下运行压缩oops。这可能有助于在一些病态的角落案例中减少元空间内存占用。一般来说,建议不要使用这些开关。

注意,压缩对象指针需要Java堆<32G。因此,如果Java堆>=32G,压缩oop将被关闭,这也将关闭压缩类指针。

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/405.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册