为何移除持久代

  • 它的大小是在启动时固定好的, 很难进行调优 -XX:MaxPermSize(默认64M)
  • HotSpot 的内部类型也是Java对象: 它可能会在Full GC中被移动, 同时它对应用不透明, 且是非强类型的, 难以跟踪调试, 还需要存储元数据信息 (meta-metadata)
  • 简化 Full GC: 每一个回收器都有专门的元数据迭代器。
  • 可以在GC不进行暂停的情况下并发地释放类数据。
  • 使得原来受限于持久代的一些改进未来有可能实现。
  • 综上, jdk1.8后, 永久代被移除, 方法区移至Metaspace, 字符串常量移至Java Heap。

移除持久代后, PermGen 空间的状况

  • 这部分内存空间将被全部移除。
  • JVM 参数: PermSize 和 MaxPermSize 会被忽略并给出警告 (如果启用时设置了这两个参数)。

metaspace 的组成

  • Klass Metaspace

    • klass是class文件在jvm里的运行时数据结构, 而Klass Metaspace 就是用来存 klass的。
    • 但X.class(反射)其实是存在heap中的, 是java.lang.Class 的一个对象实例。
    • 这块内存紧挨着的Heap, 可以通过 -XX: CompressedClassSpaceSize参数来控制(默认1G)。
    • 假如开启压缩指针就不会有这块内存, 这种情况下klass都会存在NoKlass Metaspace中
    • 另外如果我们把-Xmx设置设置大于32G的话, 这块内存也会消失, 因为过大的内存会关闭压缩指针开关。
    • 这块内存最多只会存在一块。
  • NoKlass Metaspace
    • 专门用来存klass相关的其他内存, 比如method, constantPool等。
    • 这块内存是由多块内存组合起来的, 所以可以认为是不连续的内存块组成的。
    • 这块内存是必须的, 虽然叫做NoKlass Metaspace, 但是其实也可以存klass的内容。
  • Klass Metaspace 和 NoKlass Metaspace 都是所有classloader共享的
  • 所有类加载器们要分配内存, 但是每个类加载器都有一个SpaceManager, 来管理这个类加载的内存小块。
  • 如果Klass Metaspace 用完了, 就会造成 OOM, 不过一般情况下不会。
  • NoKlass Metaspace是由一块块内存慢慢组合起来的, 在没有达到限制条件的情况下, 会不断加长这条链, 让它可以持续工作。
  • 元空间与永久代之间的最大区别:
    • 元空间不再虚拟机中, 而是使用本地内存。
  • 默认情况下, 元空间大小仅受本地内存限制, 但可以通过以下参数来指定元空间的大小:
    • -XX:MetaspaceSize
    • 初始空间大小, 达到该值就会触发垃圾收集进行类型卸载
    • 同时GC会对该值进行调整: 如果释放了大量空间, 就适当降低该值, 如果释放了很少的空间, 那么在不超过MaxMetaspaceSize时, 适当提高该值。
    • -XX:MaxMetaspaceSize
    • 最大空间, 默认没有限制。
    • -XX:MinMetaspaceFreeRatio
    • 在GC之后, 最小的Metaspace剩余空间容量的百分比, 减少为分配空间所导致的垃圾收集
    • -XX:MaxMetaspaceFreeRatio
    • 在GC之后, 最大的Metaspace剩余容量的百分比, 减少为释放空间所导致的垃圾收集
    • -verbose 参数是为了获取类型加载和卸载的信息

metaspace 的特点

  • 充分利用了Java语言规范中的好处:类及相关的元数据的生命周期与类加载器的一致。
  • 每个加载器有专门的存储空间
  • 只进行线性分配
  • 不会单独回收某个类
  • 省掉了GC扫描及压缩的时间
  • 元空间里的对象的位置是固定的
  • 如果GC发现某个类加载器不再存活了, 会把相关的空间整个回收掉

metaspace 的内存分配模型

  • 绝大多数的类元数据的空间都从本地内存中分配

  • 用来描述类元数据的类(klasses)也被删除了

  • 分元数据分配了多个虚拟内存空间

  • 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些

  • 归还内存块, 释放内存块列表

  • 一旦元空间的数据被清空了, 虚拟内存的空间会被回收掉

  • 减少碎片的策略

  • JVM是如何给元数据分配虚拟内存的空间?

  • Boot CL -> Bootstrap ClassLoader

  • _mark 和 _klass指针

    • JVM中, 每个对象都有一个指向它自身类的指针, 不过这个指针只是指向具体的实现类, 而不是接口或者抽象类。

    • 对于32位的JVM:

      • _mark: 4字节常量
      • _klass: 指向类的4字节指针, 对象的内存布局中的第二个字段(_klass, 在32位JVM中, 相对对象在内存中的位置的偏移量是4, 64位的是8) 指向的是内存中对象的类定义。
    • 对于64位的JVM:

      • _mark: 8字节常量
      • _klass: 指向类的8字节指针
    • 开启了指针压缩的64位JVM:

      • _mark: 8字节常量
      • _klass: 指向类的4字节指针
  • Java对象的内存布局

    • 类指针压缩空间 (Compressed Class Pointer Space)

      • 只有是64位平台上启用了类指针压缩才会存在这个区域。
      • 为了压缩JVM对象中的_klass指针的大小, 引入了类指针压缩空间 (Compressed Class Pointer Space)
      • 压缩指针后的内存布局
      • 指针压缩概要
        • 64位平台上默认打开
        • 使用-XX:+UseCompressedOops压缩对象指针 "oops"指的是普通对象指针("ordinary" object pointers)。
        • Java堆中对象指针会被压缩成32位。 使用堆基地址(如果堆在低26G内存中的话,基地址为0)
        • 使用-XX:+UseCompressedClassPointers选项来压缩类指针
        • 对象中指向类元数据的指针会被压缩成32位
        • 类指针压缩空间会有一个基地址
  • 元空间 与 类指针压缩空间的区别

    • 类指针压缩空间只包含类的元数据,比如InstanceKlass, ArrayKlass 仅当打开了UseCompressedClassPointers选项才生效
    • 为了提高性能,Java中的虚方法表也存放到这里 这里到底存放哪些元数据的类型,目前仍在减少
    • 元空间包含类的其它比较大的元数据,比如方法,字节码,常量池等。

metaspace 内存管理

  • 元空间的内存管理由元空间虚拟机来完成。

  • 先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。

  • 在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。

  • 话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。

  • 每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。

  • 当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。

  • 在元空间的回收过程中没有重定位和压缩等操作。

  • 但是元空间内的元数据会进行扫描来确定Java引用。

  • 元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。

  • 在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。

  • 当一个类加载器不再存活,那么其持有的组块将会被释放,并返回给全局组块列表。

  • 类加载器持有的组块又会被分成多个块,每一个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。

  • 组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式连接,一旦某个虚拟内存映射区域清空,这部分内存就会返回给操作系统。

metaspace 调优

  • MaxMetaspaceSize调优

    • -XX:MaxMetaspaceSize={unlimited}
    • 元空间的大小受限于机器的内存
    • 限制类的元数据使用的内存大小, 以免出现虚拟切换以及本地内存分配失败。
    • 如果怀疑有类加载器出现泄露,应当使用这个参数。
    • 32位机器上,如果地址空间可能会被耗尽,也应当设置这个参数。
    • 元空间的初始大小是21M——这是GC的初始的高水位线,超过这个大小会进行Full GC来进行类的回收。
    • 如果启动后GC过于频繁,请将该值设置得大一些 。
    • 可以设置成和持久代一样的大小,以便推迟GC的执行时间 。
  • CompressedClassSpaceSize调优
    • 只有当-XX:+UseCompressedClassPointers 开启了才有效
    • -XX:CompressedClassSpaceSize=1G
    • 由于这个大小在启动的时候就是固定了的, 因此最好设置得大点。
    • 没有使用到的话不要进行设置
    • JVM后续可能会让该区可以动态地增长。
    • 不需要是连续的区域, 只要从基地址可达就行。
    • 可能会将更多的类元信息放回元空间中。
    • 未来会基于PredictedLoadedClassCount的值来自动地设置该空间的大小。
    • Metaspace VM管理Metaspace空间的增长。
    • 但有时你会想通过在命令行显示的设置参数-XX:MaxMetaspaceSize来限制Metaspace空间的增长。
    • 默认情况下,-XX:MaxMetaspaceSize并没有限制,因此,在技术上,Metaspace的尺寸可以增长到交换空间,而你的本地内存分配将会失败。
    • 每次垃圾收集之后,Metaspace VM会自动的调整high watermark,推迟下一次对Metaspace的垃圾收集。
    • 这两个参数,-XX:MinMetaspaceFreeRatio和-XX:MaxMetaspaceFreeRatio,类似于GC的FreeRatio参数,可以放在命令行。

metaspace 可以使用的工具

  • 针对Metaspace,JDK自带的一些工具做了修改来展示Metaspace的信息:

  • jmap -clstats :打印类加载器的统计信息(取代了在JDK8之前打印类加载器信息的permstat)。

  • jstat -gc :Metaspace的信息也会被打印出来。

  • jcmd GC.class_stats:这是一个新的诊断命令,可以使用户连接到存活的JVM,转储Java类元数据的详细统计。

  • 这次就用Zookeeper进程查看一下结果了:

  • jmap -clstats 示例

    root@ronnie01:~# jmap -clstats 1518
    Attaching to process ID 1518, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.202-b08
    finding class loader instances ..done.
    computing per loader stat ..done.
    please wait.. computing liveness...............liveness analysis may be inaccurate ...
    class_loader classes bytes parent_loader alive? type <bootstrap> 1673 2966193 null live <internal>
    0x00000000ec2982c8 1 880 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec2992a8 1 1471 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec298200 1 1474 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec2991e0 1 1471 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec299500 1 773 null dead javax/management/remote/rmi/NoCallStackClassLoader@0x00000001000c9a90
    0x00000000ec288918 546 1001934 0x00000000ec290198 live sun/misc/Launcher$AppClassLoader@0x000000010000f8d8
    0x00000000ec290198 0 0 null live sun/misc/Launcher$ExtClassLoader@0x000000010000fc80
    0x00000000ec298458 1 880 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec299118 1 880 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec299438 1 1471 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec2995d8 1 742 null dead javax/management/remote/rmi/NoCallStackClassLoader@0x00000001000c9a90
    0x00000000ec4706d0 0 0 0x00000000ec288918 live java/util/ResourceBundle$RBClassLoader@0x00000001001000b8
    0x00000000ec298390 1 880 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028
    0x00000000ec299370 1 1471 null dead sun/reflect/DelegatingClassLoader@0x000000010000a028 total = 15 2230 3980520 N/A alive=4, dead=11 N/A
  • jstat -gc 示例

    root@ronnie01:~# jstat -gc 1518
    S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
    2560.0 2560.0 0.0 2528.0 15872.0 11145.9 42496.0 1918.0 12032.0 11678.5 1536.0 1371.2 1 0.104 0 0.000 0.104
  • jcmd GC.class_stats 示例

- JDK1.8需要指定参数  *XX:+UnlockDiagnosticVMOptions*  进入诊断状态才能执行, 然后jdk9又把该要求移除了......

提高 GC 的性能

  • Full GC中,元数据指向元数据的那些指针都不用再扫描了。很多复杂的元数据扫描的代码(尤其是CMS里面的那些)都删除了。
  • 元空间只有少量的指针指向Java堆。这包括:类的元数据中指向java/lang/Class实例的指针;数组类的元数据中,指向java/lang/Class集合的指针。
  • 没有元数据压缩的开销
  • 减少了根对象的扫描(不再扫描虚拟机里面的已加载类的字典以及其它的内部哈希表)
  • 减少了Full GC的时间
  • G1回收器中,并发标记阶段完成后可以进行类的卸载

metaspace 的问题

  • 元空间虚拟机采用了组块分配的形式,同时区块的大小由类加载器类型决定。
  • 类信息并不是固定大小,因此有可能分配的空闲区块和类需要的区块大小不同,这种情况下可能导致碎片存在。
  • 元空间虚拟机目前并不支持压缩操作,所以碎片化是目前最大的问题。

metaspace 元空间的更多相关文章

  1. JVM metaspace元空间

    元空间的本质和永久代类似,都是对JVM规范中方法区的实现. 元空间不在虚拟机中,而是使用本地内存. 用于元空间的JVM参数:   -XX:MetaspaceSize=N 初始化Metaspace大小, ...

  2. Metaspace 之二--Java 8的元空间(metaspace)、metaspace监控方法

    很多开发者都在其系统中见过“java.lang.OutOfMemoryError: PermGen space”这一问题.这往往是由类加载器相关的内存泄漏以及新类加载器的创建导致的,通常出现于代码热部 ...

  3. jdk源码剖析五:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)

    目录 1.背景 2.为什么废弃永久代(PermGen) 3.深入理解元空间(Metaspace) 4.总结 ========正文分割线===== 一.背景 1.1 永久代(PermGen)在哪里? 根 ...

  4. JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)

    1.背景 2.为什么废弃永久代(PermGen) 3.深入理解元空间(Metaspace) 4.总结 ========正文分割线===== 一.背景 1.1 永久代(PermGen)在哪里? 根据,h ...

  5. Java8内存模型—永久代(PermGen)和元空间(Metaspace)(转)

    Java8内存模型—永久代(PermGen)和元空间(Metaspace) 查看原文点击传送门:http://www.cnblogs.com/paddix/p/5309550.html 提示:本文做了 ...

  6. Java8内存模型—永久代(PermGen)和元空间(Metaspace)

    一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 1.虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建.栈里面存着的是一种叫“栈 ...

  7. (转)Java8内存模型—永久代(PermGen)和元空间(Metaspace)

    背景:介绍java8中永久代到元空间的转变. Java8内存模型—永久代(PermGen)和元空间(Metaspace) 一.JVM 内存模型 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法 ...

  8. JVM深入:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)(转)

    转载自:https://www.cnblogs.com/yulei126/p/6777323.html 1.背景 2.为什么废弃永久代(PermGen) 3.深入理解元空间(Metaspace) 4. ...

  9. Metaspace 之一:Metaspace整体介绍(永久代被替换原因、元空间特点、元空间内存查看分析方法)

    回顾 根据JVM内存区域的划分,简单的画了下方的这个示意图.区域主要分为两大块,一块是堆区(Heap),我们所New出的对象都会在堆区进行分配,在C语言中的malloc所分配的方法就是从Heap区获取 ...

随机推荐

  1. USACO 2019 December Contest 随记

    Forewords 今年 USACO 的比赛变化挺大的,有部分分了,而且不再是固定十个点了(部分分只说这几个点满足这几个性质,以为十个点的我还高兴了一会,一提交,...),除此之外居然赛后还排名了.这 ...

  2. java 获取(格式化)日期格式

    // 参考: https://www.cnblogs.com/blog5277/p/6407463.htmlpublic class DateTest { // 支持时分秒 private stati ...

  3. 如何通过DICOM的tag来判断3D图像的方向

    在DICOM标准里,有三个TAG与成像的方向相关. 参考来源:Kitware关于DICOM方向的说明 http://public.kitware.com/IGSTKWIKI/index.php/DIC ...

  4. boost::program_options 解析命令行参数

    源码: #include <boost/program_options.hpp> namespace po = boost::program_options; int main(int a ...

  5. 机器学习(ML)十一之CNN各种模型

    深度卷积神经网络(AlexNet) 在LeNet提出后的将近20年里,神经网络一度被其他机器学习方法超越,如支持向量机.虽然LeNet可以在早期的小数据集上取得好的成绩,但是在更大的真实数据集上的表现 ...

  6. 「CF286C」Main Sequence

    传送门 Luogu 解题思路 看到正负号相互抵消,很容易联想到括号匹配和栈. 但由于题目钦定了一些位置只能是负数,所以我们可以这样考虑: 把负数视为右括号,正数视为左括号,然后开一个栈,从右往左遍历, ...

  7. java与MySQL数据库的连接

    java与MySQL数据库的连接 1.数据库的安装和建立参见上一篇博客中的第1,2步骤.(http://blog.csdn.net/nuptboyzhb/article/details/8043091 ...

  8. Vue和React之间关于注册组件和组件间传值的区别

    注册组件 Vue中:1.引入组件:2.在components中注册组件:3.使用组件; React中:1.引入组件:2.使用组件; 子父传值 Vue中: 父组件向子组件传值: 1.在父组件中绑定值:2 ...

  9. JavaScript 词法句法

    JavaScript 中的几个重要概念 JavaScript 遵循 ECMA-262 规范,目前其最新版是 ECMAScript 2018,而获得所有主流浏览器完全支持的则是 ECMAScript 5 ...

  10. 苹果vs中国竞争者:瘦死的骆驼比马大?

    前不久,苹果调整2019年第一财季的营收指引,预计第一季度毛利率为38%,相关收入大约为55亿美元,全年总体营收约为840亿美元,运营开支约为87亿美元.针对2019年的运营状况,库克亲自给投资者写了 ...