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. 这部分默认放在Compressed Class Pointer Space中,是一块连续的内存区域,
    紧接着Heap,和之前的perm一样。通过-XX:CompressedClassSpaceSize来控制这块内存的大小,默认是1G。
    下图展示了对象的存储模型,_mark是对象的Mark Word,_klass是元数据指针
    has ccs.jpg
  3. Compressed Class Pointer Space不是必须有的,如果设置了-XX:-UseCompressedClassPointers,或者-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:+UseCompressedOops压缩对象指针, oops指的是普通对象指针(ordinary object pointers), 会被压缩成32位。
  3. 设置-XX:+UseCompressedClassPointers压缩类指针,会被压缩成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
CompressedClassSpaceSize
MinMetaspaceExpansion
MaxMetaspaceExpansion
MinMetaspaceFreeRatio
MaxMetaspaceFreeRatio
UseLargePagesInMetaspace
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分配的内存则是固定大小。

CompressedClassSpaceSize

Compressed Class Pointer Space区域的大小,默认1G。
如果设置了-XX:-UseCompressedClassPointers,或者-Xmx设置大于32G,则这个参数不生效。

MinMetaspaceExpansion

MinMetaspaceExpansion和MaxMetaspaceExpansion这两个参数这两个参数和扩容其实并没有直接的关系,并不是为了增大committed的内存。
主要是在比较特殊的场景下救急使用,增大触发metaspace GC的阈值,延迟GC的发生。
默认332.8K,增大触发metaspace GC阈值的最小要求。
如果需要分配的内存小于MinMetaspaceExpansion,则将metaspace GC的阈值提升MinMetaspaceExpansion。

MaxMetaspaceExpansion

默认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之后used的量来算,主要是担心可能出现committed的量超过了触发metaspaceGC的阈值,这种情况一旦发生会很危险,会不断做gc,这应该是jdk8在某个版本之后才修复的bug

MaxMetaspaceFreeRatio

默认70,这个参数和上面的参数基本是相反的,是为了避免触发metaspaceGC的阈值过大,而想对这个值进行缩小。
这个参数在gc之后committed的内存比较小的时候并且离触发metaspaceGC的阈值比较远的时候,调整会比较明显。

UseLargePagesInMetaspace

默认false,这个参数是说是否在metaspace里使用LargePage,一般情况下我们使用4KB的page size,这个参数依赖于UseLargePages这个参数开启,不过这个参数我们一般不开。

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两者总共的reserved的内存大小,比如默认情况下Klass Metaspace是通过CompressedClassSpaceSize这个参数来reserved 1G的内存,NoKlass Metaspace默认reserved的内存大小是2* InitialBootClassLoaderMetaspaceSize
  • CCSMX表示Klass Metaspace reserved的内存大小

参考资料
https://www.jianshu.com/p/92a5fbb33764
https://www.jianshu.com/p/1a0b4bf8d498
https://www.cnblogs.com/duanxz/p/3520829.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 概述 metaspace,顾名思义,元数据空间,专门用来存元数据的,它是jdk8里特有的数据结构用来替代perm,...
    你假笨阅读 8,858评论 1 24
  • 目录 一、背景 二、G1垃圾收集器特性 三、G1执行步骤 四、G1基本参数 四、G1日志解释 六、基本原理 七、...
    爱吃糖果阅读 3,220评论 0 0
  • jdk8里特有的数据结构用来替代perm metaspace其实由两大部分组成 Klass Metaspace, ...
    01010100阅读 1,486评论 0 1
  • Java 虚拟机有自己完善的硬件架构, 如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM 屏蔽了与具体操作系...
    尹小凯阅读 1,685评论 0 10
  • Java HotSpot 虚拟机是 Java SE 平台的一个核心组件。它实现 Java 虚拟机规范,并作为 Ja...
    大佛爱读书阅读 2,719评论 1 0