Meta Space是JDK1.8引入的,在JDK1.8使用的是方法区,永久代(Permnament Generation)。
元空间存储的是元信息,使用的是操作系统的本地内存(Metaspace与PermGen之间最大的区别),可以是不连续的,由元空间虚拟机进行管理。可以产生OutOfMemoryError

1、元空间的特点

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

2、元空间的内存分配模型

  • 绝大多数的类元数据的空间都从本地内存中分配
  • 用来描述类元数据的类(klasses)也被删除了
  • 分元数据分配了多个虚拟内存空间
  • 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些
  • 归还内存块,释放内存块列表
  • 一旦元空间的数据被清空了,虚拟内存的空间会被回收掉
  • 减少碎片的策略

我们来看下JVM是如何给元数据分配虚拟内存的空间的

你可以看到虚拟内存空间是如何分配的(vs1,vs2,vs3) ,以及类加载器的内存块是如何分配的。CL是Class Loader的缩写。

理解_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位平台上启用了类指针压缩才会存在这个区域。对于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中的虚方法表也存放到这里 这里到底存放哪些元数据的类型,目前仍在减少

  • 元空间包含类的其它比较大的元数据,比如方法,字节码,常量池等。

3、元空间内存管理

元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。 
准确的来说,每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。在元空间的回收过程中没有重定位和压缩等操作。但是元空间内的元数据会进行扫描来确定Java引用。 
元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。当一个类加载器不再存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分成多个块,每一个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式连接,一旦某个虚拟内存映射区域清空,这部分内存就会返回给操作系统。

上图展示的是虚拟内存映射区域如何进行元组块的分配。类加载器1和3表明使用了反射或者为匿名类加载器,他们使用了特定大小组块。 而类加载器2和4根据其内部条目的数量使用小型或者中型的组块。

4、Metaspace相关参数

(1)-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

(2)-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

如果没有使用-XX:MaxMetaspaceSize来设置类的元数据的大小,其最大可利用空间是整个系统内存的可用空间。JVM也可以增加本地内存空间来满足类元数据信息的存储。 但是如果没有设置最大值,则可能存在bug导致Metaspace的空间在不停的扩展,会导致机器的内存不足;进而可能出现swap内存被耗尽;最终导致进程直接被系统直接kill掉。如果类元数据的空间占用达到MaxMetaspaceSize设置的值,将会触发对象和类加载器的垃圾回收。

(3)-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集

(4)-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

参考文章:

(1)https://www.cnblogs.com/williamjie/p/9558094.html

(2)java面试:你真的知道metaspace?? https://www.jianshu.com/p/cd34d6f3b5b4

Hotspot的Metaspace的更多相关文章

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

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

  2. Metaspace 之一--java8 去掉 perm 用 Metaspace 来替代

    正如大家所知,JDK 8 Early Access版已经提供下载.这使开发者可以体验Java8的新特性.其中之一,是Oracle从JDK7发布以来就一直宣称的要完全移除永久代空间.例如,字符串内部池, ...

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

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

  4. java8 去掉 perm 用 Metaspace 来替代

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt266 正如大家所知,JDK 8 Early Access版已经提供下载.这使 ...

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

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

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

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

  7. Java虚拟机16:Metaspace

    被废弃的持久代 想起之前面试的时候有面试官问起过我一个问题:Java 8为什么要废弃持久代即Metaspace的作用.由于当时使用的Java 7且研究重心不在JVM上,一下没有回答上来,今天突然想起这 ...

  8. JDK1.8-Java虚拟机运行时数据区域和HotSpot虚拟机的内存模型

    目录 介绍 官方文档规定的运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈溢出 Java堆 演示堆内存溢出 方法区 运行时常量池 演示方法区溢出 HotSpot虚拟机的内 ...

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

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

随机推荐

  1. Linux内核调优部分参数说明

    #接收套接字缓冲区大小的默认值(以字节为单位). net.core.rmem_default = 262144 #接收套接字缓冲区大小的最大值(以字节为单位). net.core.rmem_max = ...

  2. 使用python远程连接数据库

    根据web连接服务的原理,我们可以通过一台电脑连接我们另一台电脑上的数据库 一.开启数据库的权限1.Mysql:1)修改访问权限首先修改可以访问的ip,把‘localhost’全部修改为‘%’,打开c ...

  3. python正确使用异常处理机制

    一.不要过度使用异常 不可否认,Python 的异常机制确实方便,但滥用异常机制也会带来一些负面影响.过度使用异常主要表现在两个方面: 把异常和普通错误混淆在一起,不再编写任何错误处理代码,而是以简单 ...

  4. Java向MySQL新增记录时间误差问题

    参考文档 https://www.jianshu.com/p/115861aad147 https://blog.csdn.net/ai932820942/article/details/845804 ...

  5. Linq分批次,每组1000条

    /// <summary> /// 分组插入每次插入1000 /// </summary> /// <param name="data">< ...

  6. moodle3.7上传中文文件,无法引用,图片不显示

    初始安装moodle3.7 上传图片,名称为中文时,无法引用图片,图片不显示.这里采用修改moodle根目录下的config.php文件, 添加了变量$CFG->slasharguments = ...

  7. requests方法中content和text区别

    requests对象的get和post方法都会返回一个Response对象,这个对象里面存的是服务器返回的所有信息,包括响应头,响应状态码等.其中返回的网页部分会存在.content和.text两个对 ...

  8. Mac切换root用户

    sudo su 然后输入密码,用户名显示sh-3.2#,这里的#代表的含义就是具有root权限 这时,再输入 su - 就可以进入root用户 那接下来,如果我们要切换至普通用户该怎么做? su - ...

  9. Linq实现字符串拼接多条件查询

    Linq实现字符串拼接多条件查询 开发过程中,为提升用户体验,经常会使用到多条件查询,本篇博客介绍如何使用Linq实现字符串拼接多条件查询 一般SQL字符串拼接 1 string sql = &quo ...

  10. 选美?作秀?MES系统的选择更应该从实际出发

    MES选型不是做秀,不是选美. 如今不少企业在信息化推广应用过程中面面求好.追求完美,用意没错,然而在MES开发过程中,软件商不可能将今后各种可能出现的问题考虑周全,不可能将系统做到十全十美.随着系统 ...