`
deepinmind
  • 浏览: 450825 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:41554
社区版块
存档分类
最新评论

Java 8的元空间

阅读更多
本文我们将会介绍JVM的一个更新,这就是持久代的移除。我们会介绍为什么需要移除持久代,以及它的替代者,元空间(metaspace)。这是上一篇文章内存管理之垃圾回收的续集。

Java 6中的堆结构是这样的:



持久代

持久代中包含了虚拟机中所有可通过反射获取到的数据,比如Class和Method对象。不同的Java虚拟机之间可能会进行类共享,因此持久代又分为只读区和读写区。

JVM用于描述应用程序中用到的类和方法的元数据也存储在持久代中。JVM运行时会用到多少持久代的空间取决于应用程序用到了多少类。除此之外,Java SE库中的类和方法也都存储在这里。

如果JVM发现有的类已经不再需要了,它会去回收(卸载)这些类,将它们的空间释放出来给其它类使用。Full GC会进行持久代的回收。

- JVM中类的元数据在Java堆中的存储区域。
- Java类对应的HotSpot虚拟机中的内部表示也存储在这里。
- 类的层级信息,字段,名字。
- 方法的编译信息及字节码。
- 变量
- 常量池和符号解析

持久代的大小

- 它的上限是MaxPermSize,默认是64M
- Java堆中的连续区域 : 如果存储在非连续的堆空间中的话,要定位出持久代到新对象的引用非常复杂并且耗时。卡表(card table),是一种记忆集(Remembered Set),它用来记录某个内存代中普通对象指针(oops)的修改。
- 持久代用完后,会抛出OutOfMemoryError "PermGen space"异常。解决方案:应用程序清理引用来触发类卸载;增加MaxPermSize的大小。
- 需要多大的持久代空间取决于类的数量,方法的大小,以及常量池的大小。

为什么移除持久代

- 它的大小是在启动时固定好的——很难进行调优。-XX:MaxPermSize,设置成多少好呢?
- HotSpot的内部类型也是Java对象:它可能会在Full GC中被移动,同时它对应用不透明,且是非强类型的,难以跟踪调试,还需要存储元数据的元数据信息(meta-metadata)。
- 简化Full GC:每一个回收器有专门的元数据迭代器。
- 可以在GC不进行暂停的情况下并发地释放类数据。
- 使得原来受限于持久代的一些改进未来有可能实现

那么JVM的元数据都去哪儿了?



元空间(metaspace)

持久代的空间被彻底地删除了,它被一个叫元空间的区域所替代了。持久代删除了之后,很明显,JVM会忽略PermSize和MaxPermSize这两个参数,还有就是你再也看不到java.lang.OutOfMemoryError: PermGen error的异常了。

JDK 8的HotSpot JVM现在使用的是本地内存来表示类的元数据,这个区域就叫做元空间。

元空间的特点:

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

元空间的内存分配模型

- 绝大多数的类元数据的空间都从本地内存中分配
- 用来描述类元数据的类也被删除了
- 分元数据分配了多个虚拟内存空间
- 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; 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中的虚方法表也存放到这里
          这里到底存放哪些元数据的类型,目前仍在减少
         
- 元空间包含类的其它比较大的元数据,比如方法,字节码,常量池等。



元空间的调优

使用-XX:MaxMetaspaceSize参数可以设置元空间的最大值,默认是没有上限的,也就是说你的系统内存上限是多少它就是多少。-XX:MetaspaceSize选项指定的是元空间的初始大小,如果没有指定的话,元空间会根据应用程序运行时的需要动态地调整大小。


MaxMetaspaceSize的调优

- -XX:MaxMetaspaceSize={unlimited}
- 元空间的大小受限于你机器的内存
- 限制类的元数据使用的内存大小,以免出现虚拟内存切换以及本地内存分配失败。如果怀疑有类加载器出现泄露,应当使用这个参数;32位机器上,如果地址空间可能会被耗尽,也应当设置这个参数。
- 元空间的初始大小是21M——这是GC的初始的高水位线,超过这个大小会进行Full GC来进行类的回收。
- 如果启动后GC过于频繁,请将该值设置得大一些
- 可以设置成和持久代一样的大小,以便推迟GC的执行时间


CompressedClassSpaceSize的调优

- 只有当-XX:+UseCompressedClassPointers开启了才有效
- -XX:CompressedClassSpaceSize=1G
- 由于这个大小在启动的时候就固定了的,因此最好设置得大点。
- 没有使用到的话不要进行设置
- JVM后续可能会让这个区可以动态的增长。不需要是连续的区域,只要从基地址可达就行;可能会将更多的类元信息放回到元空间中;未来会基于PredictedLoadedClassCount的值来自动的设置该空间的大小


元空间的一些工具

- jmap -permstat改成了jmap -clstats。它用来打印Java堆的类加载器的统计数据。对每一个类加载器,会输出它的名字,是否存活,地址,父类加载器,以及它已经加载的类的数量及大小。除此之外,驻留的字符串(intern)的数量及大小也会打印出来。
- jstat -gc,这个命令输出的是元空间的信息而非持久代的
- jcmd <pid> GC.class_stats提供类元数据大小的详细信息。使用这个功能启动程序时需要加上-XX:+UnlockDiagnosticVMOptions选项。


提高GC的性能

如果你理解了元空间的概念,很容易发现GC的性能得到了提升。

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


总结

- Hotspot中的元数据现在存储到了元空间里。mmap中的内存块的生命周期与类加载器的一致。
- 类指针压缩空间(Compressed class pointer space)目前仍然是固定大小的,但它的空间较大
- 可以进行参数的调优,不过这不是必需的。
- 未来可能会增加其它的优化及新特性。比如, 应用程序类数据共享;新生代GC优化,G1回收器进行类的回收;减少元数据的大小,以及JVM内部对象的内存占用量。

译注:这有点像一篇读书笔记,比较琐碎。。



原创文章转载请注明出处:http://it.deepinmind.com

英文原文链接
6
1
分享到:
评论
2 楼 fair_jm 2014-05-15  
先码后看~瞥了眼翻译地挺好~~
1 楼 alvin198761 2014-05-14  
你长篇大论讲完了,我却问你:大神啊,你第一句我就没有搞懂啊,什么是持久代?

相关推荐

    java 8下载,版本 8u351, windows各版本

    1. 首先,确保你的计算机满足Java 8的系统需求,包括处理器、内存和磁盘空间。 2. 下载适合你Windows版本的安装包,例如,如果是64位操作系统,应选择64位版本的`jdk8-windows-8u351`。 3. 运行安装程序,按照提示...

    Java面试题--java8

    在Java 8中,元空间(MetaSpace)取代了永久代(PermGen Space)。永久代是JVM中存储类元数据的区域,但在Java 8中,元数据被存储在本机内存的MetaSpace中,不再占用堆内存,从而避免了内存溢出的问题。元空间的大小...

    java读取metadata元信息

    在处理XML或JSON文件时,元数据可能是文档的结构信息,如XML的命名空间、元素属性或JSON对象的键值。Java的`javax.xml.parsers.DocumentBuilderFactory`和`com.fasterxml.jackson.databind.ObjectMapper`等库可以...

    The Java Language Specification Java SE 8 Edition Java编程规范

    注解是一种元数据,可以用于代码的编译时和运行时处理,Java 8增加了对类型注解的支持。 十、JVM与内存管理 Java虚拟机(JVM)是Java程序的运行环境,负责内存管理和垃圾回收。Java 8改进了垃圾收集器,如G1收集器...

    java的时间空间…………

    Java 8引入了新的日期时间API(`java.time`包),极大地改善了这一情况。新API提供了更友好的类如`LocalDate`、`LocalTime`、`LocalDateTime`、`ZonedDateTime`等,以及诸如`Duration`和`Period`用于处理时间间隔。...

    java8的64位安装包jdk-8u181-windows-x64

    Java 8是Oracle公司推出的Java开发工具包(Java Development Kit,简称JDK)的一个重要版本,对于软件开发者来说,它是构建、运行Java应用程序的基础。64位版本的JDK适用于处理大量内存和高性能计算需求的环境,因为...

    JAVA8虚拟机(jvm)规范_Chinese version.rar

    6. **元空间(Metaspace)**:Java8替代了永久代(PermGen),使用元空间来存储类元数据。元空间位于 native memory 中,避免了永久代因类数量过多导致的 OutOfMemoryError。 7. **并行与并发**:JVM支持多线程执行...

    OnJava8.zip

    《OnJava8.zip》是一个关于Java 8编程的压缩包,包含了深入讲解Java 8特性和新功能的文档。在本篇文章中,我们将探讨Java 8的重要更新,包括Lambda表达式、函数式接口、Stream API、日期与时间API以及默认方法等核心...

    Java Platform SE 8 jdk8 API文档 CHM格式

    9. **类型注解**:Java 8允许在类型声明(包括参数、返回值、字段和局部变量)上使用注解,增强了代码的可验证性和元数据信息。 10. **Nashorn JavaScript引擎**:Java 8内置了一个JavaScript引擎,允许Java程序...

    最新Java JDK 8免安装版(Linux 32位)

    对于Linux 32位系统的用户来说,免安装版的JDK 8尤其方便,因为它无需经过复杂的安装过程,可以直接在系统上运行,节省了时间和硬盘空间。 首先,我们来详细了解一下Java JDK 8中的核心组件: 1. **Java编译器**:...

    Java8新特性之再见Permgen_动力节点Java学院整理

    Java 8引入了许多新特性,其中一项重要的变化就是告别了 PermGen 空间,转而采用元空间 (Metaspace) 来存储类元数据。PermGen space,全称为Permanent Generation space,是早期Java HotSpot虚拟机中的一个内存区域...

    JAVA 8种排序介绍及实现

    在编程领域,排序算法是基础且重要的数据处理技术。本文将介绍两种常见的排序算法:直接插入排序和希尔排序,并通过Java代码...在实际开发中,我们通常会根据数据规模、稳定性、空间复杂度等因素选择合适的排序算法。

    java-jdk-8u51-windows-x64.exe.zip

    Java JDK 8u51是Oracle公司发布的一个针对Windows x64操作系统的64位Java开发工具包。这个版本在2015年7月20日被标记为最新的,对于开发者来说,它是构建和运行Java应用程序的基础。下面将详细阐述Java JDK 8u51的...

    java8 32位 64位 jdk8 最新下载地址

    ### Java 8 32位与64位JDK8最新下载及选择指南 #### Java 8概述 Java 8是Java平台标准版(Java SE)的一个重要版本,由Oracle公司于2014年发布。它引入了许多新特性,如Lambda表达式、流(Stream)API、日期和时间...

    xtreme for java连接oracle spatial 空间数据库.doc

    本篇将深入探讨如何使用xtreme for Java来连接Oracle Spatial空间数据库,帮助开发者实现高效的数据交互。 首先,了解xtreme for Java。xtreme for Java是一款Java开发工具,它提供了对各种数据库系统的访问接口,...

    k8s的Java客户端的帮助文档,从官网代码生成的JavaDoc

    在使用Kubernetes Java客户端时,首先需要创建一个`Config`对象,配置集群的API服务器地址、认证信息(如证书、token)、命名空间等。然后通过`ApiClient`构建客户端实例,这是与K8s API交互的基础。 2. **API资源...

    java language specification 8 (JLS8)

    9. 包和模块系统:JLS8介绍了包的概念,它是命名空间和访问控制的机制。此外,Java 9引入了模块系统(Project Jigsaw),虽然JLS8没有详细涵盖,但对了解Java的模块化有一定帮助。 10. 编译和运行:JLS8规定了源...

    Java 8 内存模型.pdf

    在详细介绍Java 8内存模型之前,需要了解的是,JVM(Java虚拟机)在启动时,操作系统会为JVM进程分配一系列内存区域,这些内存区域包括堆(Heap)、元空间(MetaSpace)、线程堆栈(Thread Stack)、共享库(Shared ...

    最新 java se8 虚拟机 详解

    8. **MetaSpace替代PermGen**:Java 8中,永久代(Permanent Generation)被MetaSpace取代,这解决了 PermGen 空间溢出的问题,并允许JVM动态调整元空间大小。 9. ** invokedynamic指令**:Java 8进一步利用了...

    Java Runtime Environment 8u201 - jre8 64位

    标题提到的"Java Runtime Environment 8u201"是指Java 8的第201次更新,这是一个重要的里程碑,因为它是Oracle在开始对商业用途收费之前提供的最后一个免费稳定版本。 Java 8是Java语言的一个重大升级,引入了诸多...

Global site tag (gtag.js) - Google Analytics