【JVM】元空间详解Metaspace

更新时间:2023-07-16 17:21:04 阅读: 评论:0

【JVM】元空间详解Metaspace
⼀、从⽅法区(PermGen)到元空间(Metaspace)
⽅法区(PermGen)
1. JDK1.8以前的HotSpot JVM有⽅法区,也叫永久代(permanent generation)。
2. ⽅法区⽤于存放已被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码。
3. ⽅法区是⼀⽚连续的堆空间,通过-XX:MaxPermSize来设定永久代最⼤可分配空间,当JVM加载的类信息容量超过了这个值,会报
OOM:PermGen错误。
4. 永久代的GC是和⽼年代(old generation)捆绑在⼀起的,⽆论谁满了,都会触发永久代和⽼年代的垃圾收集。
5. JDK1.7开始了⽅法区的部分移除:符号引⽤(Symbols)移⾄native heap,字⾯量(interned strings)和静态变量(class statics)移⾄java
heap。
为什么要⽤Metaspace替代⽅法区
随着动态类加载的情况越来越多,这块内存变得不太可控,如果设置⼩了,系统运⾏过程中就容易出现内存溢出,设置⼤了⼜浪费内存。
⼆、Metaspace的组成
Metaspace由两⼤部分组成:Klass Metaspace和NoKlass Metaspace。
Klass Metaspace
1. Klass Metaspace就是⽤来存klass的,就是class⽂件在jvm⾥的运⾏时数据结构(不过我们看到的类似A.class其实是存在heap⾥的,
是java.lang.Class的对象实例)。
2. 这部分默认放在Compresd Class Pointer Space中,是⼀块连续的内存区域,
紧接着Heap,和之前的perm⼀样。通过-XX:CompresdClassSpaceSize来控制这块内存的⼤⼩,
默认是1G。
下图展⽰了对象的存储模型,_mark是对象的Mark Word,_klass是元数据指针
has ccs.jpg
3. Compresd Class Pointer Space不是必须有的,如果设置了-XX:-UCompresdClassPointers,或者-Xmx设置⼤于32G,就不会
有这块内存,这种情况下klass都会存在NoKlass Metaspace⾥。
no ccs.jpg
NoKlass Metaspace
1. NoKlass Metaspace专门来存klass相关的其他的内容,⽐如method,constantPool等,可以由多块不连续的内存组成。
2. 这块内存是必须的,虽然叫做NoKlass Metaspace,但是也其实可以存klass的内容,上⾯已经提到了对应场景。
盐英语怎么说
3. NoKlass Metaspace在本地内存中分配。
指针压缩
1. 64位平台上默认打开
西游记经典片段2. 设置-XX:+UCompresdOops压缩对象指针, oops指的是普通对象指针(ordinary object pointers),会被压缩成32位。
3. 设置-XX:+UCompresdClassPointers压缩类指针,会被压缩成32位。特种劳动防护用品
三、Metaspace内存管理
1. 在metaspace中,类和其元数据的⽣命周期与其对应的类加载器相同,只要类的类加载器是存活的,在Metaspace中的类元数据也是存
活的,不能被回收。
2. 每个加载器有单独的存储空间。
3. 省掉了GC扫描及压缩的时间。
4. 当GC发现某个类加载器不再存活了,会把对应的空间整个回收。
Metaspace VM使⽤⼀个块分配器(chunking allocator)来管理Metaspace空间的内存分配。块的⼤⼩依赖于类加载器的类型。
Metaspace VM中有⼀个全局的可使⽤的块列表(a global free list of chunks)。当类加载器需要⼀个块的时候,类加载器从全局块列表中取出⼀个块,添加到它⾃⼰维护的块列表中。当类加载器死亡,它的块将会被释放,归还给全局的块列表。
块(chunk)会进⼀步被划分成blocks,每个block存储⼀个元数据单元(a unit of metadata)。Chunk中Blocks的是分配线性的(pointer bump)。这些chunks被分配在内存映射空间(memory mapped(mmapped) spaces)之外。在⼀个全局的虚拟内存映射空间(global virtual mmapped spaces)的链表,当任何虚拟空间变为空时,就将该虚拟空间归还回操作系统。
metachunks.jpg
上⾯这幅图展⽰了Metaspace使⽤metachunks在mmapeded virual spaces分配的情形。
四、metaspace的主要参数
MetaspaceSize
MaxMetaspaceSize
CompresdClassSpaceSize
MinMetaspaceExpansion
MaxMetaspaceExpansion
MinMetaspaceFreeRatio
MaxMetaspaceFreeRatio
ULargePagesInMetaspace
InitialBootClassLoaderMetaspaceSize
MetaspaceSize
metaspaceGC发⽣的初始阈值,也是最⼩阈值,默认20.8M左右,与之对⽐的主要是指Klass Metaspace与NoKlass Metaspace两块committed的内存和。
触发metaspaceGC的阈值是不断变化的:当metaspace使⽤的内存接近阈值时,会尝试增⼤阈值。metaspaceGC后也会调整阈值。MaxMetaspaceSize
由于metaspace⼤部分在本地内存中分配,默认基本是⽆穷⼤,但仍然受本地内存⼤⼩的限制。为了防⽌metaspace被⽆⽌境使⽤,建议设置这个参数。
这个参数会限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的内存⼤⼩,会保证committed的内存不会超过这个值,⼀旦超过就会触发GC。
和MaxPermSize的区别,根据MaxMetaspaceSize,并不会在jvm启动的时候分配⼀块这么⼤的内存出来,⽽根据MaxPermSize分配的内存则是固定⼤⼩。
CompresdClassSpaceSize【】
Compresd Class Pointer Space区域的⼤⼩,默认1G。
如果设置了-XX:-UCompresdClassPointers,或者-Xmx设置⼤于32G,则这个参数不⽣效。
这两个参数是相互依赖的,关系说明
UCompresdOops
UCompresdClassPointers
java8移除了permanent generation,然后class metadata存储在native memory中,其⼤⼩默认是不受限的,可以通过-
XX:MaxMetaspaceSize来限制;如果开启了-XX:+UCompresdOops及-XX:+UCompresdClassPointers(默认是开启),则UCompresdOops会使⽤32-bit的offt来代表java object的引⽤,⽽UCompresdClassPointers则使⽤32-bit的offt来代表64-bit进程中的class pointer;可以使⽤CompresdClassSpaceSize来设置这块的空间⼤⼩
开启了指针压缩,则CompresdClassSpace分配在MaxMetaspaceSize⾥头,即MaxMetaspaceSize=Compresd Class Space Size + Metaspace area (excluding the Compresd Class Space) Size
查看CompresdClassSpace的内存使⽤情况有好⼏种⽅法:
jcmd pid GC.heap_info(Metaspace为总的部分,包含了class space,⽽Metaspace area (excluding the Compresd Class Space)需要⾃⼰计算即
total-class space)
jcmd pid VM.native_memory(class为总的部分,包含了Metaspace area (excluding the Compresd Class Space)及Class Space)
使⽤JMX来获取NON_HEAP类型中的name为Metaspace及Compresd Class Space的MemoryPoolMXBean可以得到
Metaspace及Compresd Class Space的使⽤情况(JMX得到的Metaspace为总的部分,⽽Metaspace area (excluding the Compresd Class
Space)需要⾃⼰计算即total-class space)
如果是springboot应⽤,它使⽤micrometer,通过/actuator/metrics接⼝提供相关指标查询功能,其中Metaspace及Compresd Class Space在ud这个metric中
MinMetaspaceExpansion
MinMetaspaceExpansion和MaxMetaspaceExpansion这两个参数这两个参数和扩容其实并没有直接的关系,并不是为了增⼤committed的内存。
主要是在⽐较特殊的场景下救急使⽤,增⼤触发metaspace GC的阈值,延迟GC的发⽣。
默认332.8K,增⼤触发metaspace GC阈值的最⼩要求。
如果需要分配的内存⼩于MinMetaspaceExpansion,则将metaspace GC的阈值提升MinMetaspaceExpansion。
MaxMetaspaceExpansion
3人斗地主默认5.2M,增⼤触发metaspace GC阈值的最⼤要求。
如果需要分配的内存⼤于MinMetaspaceExpansion但是⼩于MaxMetaspaceExpansion,那增量就是MaxMetaspaceExpansion。
如果需要分配的内存超过了MaxMetaspaceExpansion,那增量就是MinMetaspaceExpansion加上要分配的内存⼤⼩
注:每次分配只会给对应的线程⼀次扩展触发metaspace GC阈值的机会,如果扩展了,但是还不能分配,那就只能等着做GC了。
MinMetaspaceFreeRatio
MinMetaspaceFreeRatio和下⾯的MaxMetaspaceFreeRatio,主要是影响触发metaspaceGC的阈值。
默认40,表⽰每次GC完之后,如果metaspace内存的空闲⽐例⼩于MinMetaspaceFreeRatio%,那么将尝试做扩容,增⼤触发metaspaceGC的阈值。
不过这个增量⾄少是MinMetaspaceExpansion才会做,不然不会增加这个阈值。
冬笋怎么切这个参数主要是为了避免触发metaspaceGC的阈值和gc之后committed的内存的量⽐较接近,于是将这个阈值进⾏扩⼤。
肉桂的副作用
注:这⾥不⽤gc之后ud的量来算,主要是担⼼可能出现committed的量超过了触发metaspaceGC的阈值,这种情况⼀旦发⽣会很危险,会不断做gc,这应该是jdk8在某个版本之后才修复的bug
MaxMetaspaceFreeRatio
默认70,这个参数和上⾯的参数基本是相反的,是为了避免触发metaspaceGC的阈值过⼤,⽽想对这个值进⾏缩⼩。
这个参数在gc之后committed的内存⽐较⼩的时候并且离触发metaspaceGC的阈值⽐较远的时候,调整会⽐较明显。
ULargePagesInMetaspace
360热点
默认fal,这个参数是说是否在metaspace⾥使⽤LargePage,⼀般情况下我们使⽤4KB的page size,这个参数依赖于ULargePages这个参数开启,不过这个参数我们⼀般不开。
InitialBootClassLoaderMetaspaceSize
64位下默认4M,32位下默认2200K,metasapce前⾯已经提到主要分了两⼤块,Klass Metaspace以及NoKlass Metaspace,⽽NoKlass Metaspace是由⼀块块内存组合起来的,这个参数决定了NoKlass Metaspace的第⼀个内存Block的⼤⼩,即
2*InitialBootClassLoaderMetaspaceSize,同时为bootstrapClassLoader的第⼀块内存chunk分配了InitialBootClassLoaderMetaspaceSize 的⼤⼩
五、jstat⾥的metaspace字段
我们看GC是否异常,除了通过GC⽇志来做分析之外,我们还可以通过jstat这样的⼯具展⽰的数据来分析,前⾯我公众号⾥有篇⽂章介绍了jstat这块的实现,有兴趣的可以到我的公众号你假笨⾥去翻阅下jstat的这篇⽂章。
我们通过jstat可以看到metaspace相关的这么⼀些指标,分别是
M,CCS,MC,MU,CCSC,CCSU,MCMN,MCMX,CCSMN,CCSMX
MC & MU & CCSC & CCSU
MC表⽰Klass Metaspace以及NoKlass Metaspace两者总共committed的内存⼤⼩,单位是KB,虽然从上⾯的定义⾥我们看到了是capacity,但是实质上计算的时候并不是capacity,⽽是committed,这个是要注意的
MU这个⽆可厚⾮,说的就是Klass Metaspace以及NoKlass Metaspace两者已经使⽤了的内存⼤⼩
CCSC表⽰的是Klass Metaspace的已经被commit的内存⼤⼩,单位也是KB
CCSU表⽰Klass Metaspace的已经被使⽤的内存⼤⼩
M & CCS
M表⽰的是Klass Metaspace以及NoKlass Metaspace两者总共的使⽤率,其实可以根据上⾯的四个指标算出来,即
(CCSU+MU)/(CCSC+MC)
CCS表⽰的是NoKlass Metaspace的使⽤率,也就是CCSU/CCSC算出来的
PS:所以我们有时候看到M的值达到了90%以上,其实这个并不⼀定说明metaspace⽤了很多了,因为内存是慢慢commit的,所以我们的分母是慢慢变⼤的,不过当我们committed到⼀定量的时候就不会再增长了
MCMN & MCMX & CCSMN & CCSMX
MCMN和CCSMN这两个值可以忽略,⼀直都是0
MCMX表⽰Klass Metaspace以及NoKlass Metaspace两者总共的rerved的内存⼤⼩,⽐如默认情况下Klass Metaspace是通过CompresdClassSpaceSize这个参数来rerved 1G的内存,NoKlass Metaspace默认rerved的内存⼤⼩是2* InitialBootClassLoaderMetaspaceSize
CCSMX表⽰Klass Metaspace rerved的内存⼤⼩
反不正当竞争

本文发布于:2023-07-16 17:21:04,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1084002.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:内存   阈值   分配   参数   加载   触发
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图