JVM元空间(Metaspace)
从方法区(PermGen)到元空间(Metaspace)
方法区(PermGen)
- JDK1.8以前的HotSpot JVM有方法区,也叫永久代(permanent generation)。
- 方法区用于存放已被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码。
- 方法区是一片连续的堆空间,通过-XX:MaxPermSize来设定永久代最大可分配空间,当JVM加载的类信息容量超过了这个值,会报OOM:PermGen错误。
- 永久代的GC是和老年代(old generation)捆绑在一起的,无论谁满了,都会触发永久代和老年代的垃圾收集。
- JDK1.7开始了方法区的部分移除:符号引用(Symbols)移至native heap,字面量(interned strings)和静态变量(class statics)移至java heap。
为什么要用Metaspace替代方法区
随着动态类加载的情况越来越多,这块内存变得不太可控,如果设置小了,系统运行过程中就容易出现内存溢出,设置大了又浪费内存。
Metaspace的组成
Metaspace由两大部分组成:Klass Metaspace和NoKlass Metaspace。
Klass Metaspace
Klass Metaspace就是用来存klass的,就是class文件在jvm里的运行时数据结构(不过我们看到的类似A.class其实是存在heap里的,是java.lang.Class的对象实例)。
这部分默认放在Compressed Class Pointer Space中,是一块连续的内存区域,紧接着Heap,和之前的perm一样。通过-XX:CompressedClassSpaceSize来控制这块内存的大小,默认是1G。
下图展示了对象的存储模型,_mark是对象的Mark Word,_klass是元数据指针
- Compressed Class Pointer Space不是必须有的,如果设置了-XX:-UseCompressedClassPointers,或者-Xmx设置大于32G,就不会有这块内存,这种情况下klass都会存在NoKlass Metaspace里。
NoKlass Metaspace
- NoKlass Metaspace专门来存klass相关的其他的内容,比如method,constantPool等,可以由多块不连续的内存组成。
- 这块内存是必须的,虽然叫做NoKlass Metaspace,但是也其实可以存klass的内容,上面已经提到了对应场景。
- NoKlass Metaspace在本地内存中分配。
指针压缩
- 64位平台上默认打开
- 设置-XX:+UseCompressedOops压缩对象指针, oops指的是普通对象指针(ordinary object pointers), 会被压缩成32位。
- 设置-XX:+UseCompressedClassPointers压缩类指针,会被压缩成32位。
Metaspace内存管理
- 在metaspace中,类和其元数据的生命周期与其对应的类加载器相同,只要类的类加载器是存活的,在Metaspace中的类元数据也是存活的,不能被回收。
- 每个加载器有单独的存储空间。
- 省掉了GC扫描及压缩的时间。
- 当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)的链表,当任何虚拟空间变为空时,就将该虚拟空间归还回操作系统。
上面这幅图展示了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
JVM元空间(Metaspace)的更多相关文章
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)(转)
Java8内存模型—永久代(PermGen)和元空间(Metaspace) 查看原文点击传送门:http://www.cnblogs.com/paddix/p/5309550.html 提示:本文做了 ...
- (转)Java8内存模型—永久代(PermGen)和元空间(Metaspace)
背景:介绍java8中永久代到元空间的转变. Java8内存模型—永久代(PermGen)和元空间(Metaspace) 一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法 ...
- JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap
一.intern()定义及使用 相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下: public native String inter ...
- Java8内存模型—永久代(PermGen)和元空间(Metaspace)
一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 1.虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建.栈里面存着的是一种叫“栈 ...
- 转:Java8内存模型—永久代(PermGen)和元空间(Metaspace)
一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 1.虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建.栈里面存着的是一种叫“栈 ...
- Java8内存结构—永久代(PermGen)和元空间(Metaspace)
本文转载 作者:liuxiaopeng 博客地址:https://www.cnblogs.com/paddix/p/5309550.html 一.JVM 内存结构 根据 JVM 规范,JVM 内存共分 ...
- Metaspace 之二--Java 8的元空间(metaspace)、metaspace监控方法
很多开发者都在其系统中见过“java.lang.OutOfMemoryError: PermGen space”这一问题.这往往是由类加载器相关的内存泄漏以及新类加载器的创建导致的,通常出现于代码热部 ...
- AVA 8 :从永久区(PermGen)到元空间(Metaspace)
你注意到了吗?JDK 8早期可访问版本已经提供下载了,java 开发人员可以使用java 8 提供的新的语言和运行特性来做一些实验.其中一个特性就是完全的移除永久代(Permanent Generat ...
- 面试官,Java8 JVM内存结构变了,永久代到元空间
在文章<JVM之内存结构详解>中我们描述了Java7以前的JVM内存结构,但在Java8和以后版本中JVM的内存结构慢慢发生了变化.作为面试官如果你还不知道,那么面试过程中是不是有些露怯? ...
随机推荐
- redis性能优化、内存分析及优化
redis性能优化.内存分析及优化 1.优化网络延时 2.警惕执行时间长的操作 3.优化数据结构.使用正确的算法 4.考虑操作系统和硬件是否影响性能 5.考虑持久化带来的开销 5.1 RDB 全量持久 ...
- CAS+Tomcat SSL第三方数据证书导入(jks)
首先,为CAS SERVER配置HTTPS 切换到证书 xxx.jks的目录下,查看证书的信息 keytool -list -keystore XXX.jks -storepass **** XX ...
- vim自动添加C C++ sh文件头
set foldenable set foldmethod=manual set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936 set ...
- C/C++ ===复习==函数返回值问题(集合体==网络)
按值传递 地址传递: 应该明白只有这2种传递,下面讨论函数的按值传递 #include <stdio.h> #include <stdlib.h> int add_rtVal( ...
- elasticsearch7.8权限控制和规划
由于在版本7开始,x-pack可以免费使用了,但是权限控制免费的不够细,但是控制到索引级别都基本够用了.付费的可以体验更细致的权限控制.本文的基础是已经有了es集群的基础上进行的. 官网:https: ...
- centos7.2安装图形化界面 && 在Linux上更改当前默认界面
安装环境 [root@desktop-test ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 安装过程 [root@ ...
- P1435 回文字串(DP)
题目描述 回文词是一种对称的字符串.任意给定一个字符串,通过插入若干字符,都可以变成回文词.此题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数. 比如 "Ab3bd"插 ...
- 2019牛客暑期多校训练营(第六场)J Upgrading Technology
传送门 题意: 就是给你n个技能,每个技能最高升到m级,每升一级就是耗费Cij钱,这个Cij可能是负的,如果所有技能都升到或者说超过j等级,就会获得Dj钱,这个Dj也有可能是负值,让你求你最多得到多少 ...
- HDU-6290 奢侈的旅行 (Dijkstra+堆优化)
高玩小Q不仅喜欢玩寻宝游戏,还喜欢一款升级养成类游戏.在这个游戏的世界地图中一共有nn个城镇,编号依次为11到nn.这些城镇之间有mm条单向道路,第ii 条单项道路包含四个参数ui,vi,ai,biu ...
- Python_微信支付(云开发)
一.创建云开发小程序 1.初始化云开发环境 //app.js App({ onLaunch: function () { wx.cloud.init({ //初始化云开发环境 env: 'wxypay ...