`

Java永久代去哪儿了

 
阅读更多

在Java虚拟机(以下简称JVM)中,类包含其对应的元数据,比如类的层级信息,方法数据和方法信息(如字节码,栈和变量大小),运行时常量池,已确定的符号引用和虚方法表。


在过去(当自定义类加载器使用不普遍的时候),类几乎是“静态的”并且很少被卸载和回收,因此类也可以被看成“永久的”。另外由于类作为JVM实现的一部分,它们不由程序来创建,因为它们也被认为是“非堆”的内存。


在JDK8之前的HotSpot虚拟机中,类的这些“永久的”数据存放在一个叫做永久代的区域。永久代一段连续的内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize的值来控制永久代的大小,32位机器默认的永久代的大小为64M,64位的机器则为85M。永久代的垃圾回收和老年代的垃圾回收是绑定的,一旦其中一个区域被占满,这两个区都要进行垃圾回收。但是有一个明显的问题,由于我们可以通过‑XX:MaxPermSize 设置永久代的大小,一旦类的元数据超过了设定的大小,程序就会耗尽内存,并出现内存溢出错误(OOM)。


备注:在JDK7之前的HotSpot虚拟机中,纳入字符串常量池的字符串被存储在永久代中,因此导致了一系列的性能问题和内存溢出错误。想要了解这些永久代移除这些字符串的信息,请访问这里查看。


辞永久代,迎元空间

随着Java8的到来,我们再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域就是我们要提到的元空间。


这项改动是很有必要的,因为对永久代进行调优是很困难的。永久代中的元数据可能会随着每一次Full GC发生而进行移动。并且为永久代设置空间大小也是很难确定的,因为这其中有很多影响因素,比如类的总数,常量池的大小和方法数量等。


同时,HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。将元数据从永久代剥离出来,不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化。


移除永久代的影响

由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。因此,我们就不会遇到永久代存在时的内存溢出错误,也不会出现泄漏的数据移到交换区这样的事情。最终用户可以为元空间设置一个可用空间最大值,如果不进行设置,JVM会自动根据类的元数据大小动态增加元空间的容量。


注意:永久代的移除并不代表自定义的类加载器泄露问题就解决了。因此,你还必须监控你的内存消耗情况,因为一旦发生泄漏,会占用你的大量本地内存,并且还可能导致交换区交换更加糟糕。


元空间内存管理

元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。


我们从行文到现在提到的元空间稍微有点不严谨。准确的来说,每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。在元空间的回收过程中没有重定位和压缩等操作。但是元空间内的元数据会进行扫描来确定Java引用。


元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。当一个类加载器不再存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分成多个块,每一个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式连接,一旦某个虚拟内存映射区域清空,这部分内存就会返回给操作系统。


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


元空间调优与工具

正如上面提到的,元空间虚拟机控制元空间的增长。但是有些时候我们想限制其增长,比如通过显式在命令行中设置-XX:MaxMetaspaceSize。默认情况下,-XX:MaxMetaspaceSize的值没有限制,因此元空间甚至可以延伸到交换区,但是这时候当我们进行本地内存分配时将会失败。


对于一个64位的服务器端JVM来说,其默认的–XX:MetaspaceSize值为21MB。这就是初始的高水位线。一旦触及到这个水位线,Full GC将会被触发并卸载没有用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,这个高水位线则上升。如果释放空间过多,则高水位线下降。如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志我们可以观察到Full GC多次调用。为了避免频繁的GC,建议将–XX:MetaspaceSize设置为一个相对较高的值。


经过多次GC之后,元空间虚拟机自动调节高水位线,以此来推迟下一次垃圾回收到来。


有这样两个选项 ‑XX:MinMetaspaceFreeRatio和‑XX:MaxMetaspaceFreeRatio,他们类似于GC的FreeRatio选项,用来设置元空间空闲比例的最大值和最小值。我们可以通过命令行对这两个选项设置对应的值。


下面是一些改进的工具,用来获取更多关于元空间的信息。


jmap -clstats PID 打印类加载器数据。(-clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据)。


jstat -gc LVMID 用来打印元空间的信息。具体内容如下:


jcmd PID GC.class_stats 一个新的诊断命令,用来连接到运行的JVM并输出详尽的类元数据的柱状图。


存在的问题

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


分享到:
评论

相关推荐

    携程去哪儿机票爬虫,添加ip代理请求携程和去哪儿网,Java语言

    本项目是关于使用Java语言编写的一个专门针对携程和去哪儿网的机票价格信息爬虫。这个爬虫设计的目的是为了能够有效地抓取这两个网站上的实时机票价格,帮助用户进行比价或者进行数据分析。 首先,我们来详细讲解...

    去哪儿网的java开发生态环境.pdf

    ### 去哪儿网的Java开发生态环境概览 #### 自动化发布系统 去哪儿网针对其复杂的开发环境和多变的业务需求,构建了一套自动化的发布系统来提高开发效率和减少人工错误。传统的方式是使用Eclipse进行编译,然后手动...

    去哪儿网开发岗位笔试题

    去哪儿网校园招聘,笔试题目,只拍了开发岗位的题目

    2014去哪儿网 java笔试题 手机拍

    【标题】2014去哪儿网的Java笔试题目主要涵盖了Java编程语言的基础知识、面向对象设计、数据结构与算法、并发编程以及软件工程等多个方面。这些题目旨在考察应聘者的编程能力、逻辑思维以及对Java技术栈的理解程度。...

    基于Java开发的携程去哪儿风格CalendarListView设计源码

    该项目是一款采用Java语言开发的携程去哪儿风格CalendarListView设计源码,共计包含53个文件,其中XML配置文件14个、Java源文件11个、PNG图片文件9个、Gradle文件4个、Git忽略文件3个、属性文件3个、文本文件2个、...

    去哪儿酒店数据爬取HotelSpider.zip

    【酒店管理系统】 酒店管理系统是一种用于帮助酒店...Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    携程+去哪儿爬虫Java代码,仅供参考

    "携程+去哪儿爬虫Java代码"是一个示例项目,旨在教授如何利用Java编程语言编写爬虫程序来抓取携程和去哪儿这两个知名在线旅行服务平台上的数据。在这个项目中,爬虫主要针对机票数据,提供了一个实用的方法来获取并...

    去哪儿网笔试题汇总

    2. **编程语言**:去哪儿网可能偏爱Java语言,因此【www.java1234.com】这个域名暗示了Java编程可能是主要考察对象。Java语言的关键特性,如面向对象、异常处理、集合框架、多线程编程、JVM内存模型等,都可能出现在...

    基于Java及HTML/CSS/JavaScript的15周测试去哪儿网设计源码

    该项目是一款为期15周的测试项目,旨在设计去哪儿网的源码,使用Java作为主要开发语言,并融合HTML、CSS和JavaScript技术。整个项目包含1859个文件,其中包含1745个JPG图片、32个Java源文件、26个PNG图片、12个XML...

    利用Java的反射与代理实现IOC模式

    Java的反射机制是Java编程语言的核心特性之一,它允许程序在运行时检查和操作类、接口、对象等的内部结构。反射机制使得开发者能够在运行时动态地创建对象、调用方法、访问字段,以及发现类的元数据。通过反射,我们...

    JAVA_API1.6文档(中文)

    javax.swing.plaf.metal 提供根据 Java 外观(曾经代称为 Metal)构建的用户界面对象,Java 外观是默认外观。 javax.swing.plaf.multi 提供了组合两个或多个外观的用户界面对象。 javax.swing.plaf.synth Synth 是...

    JDK8的JVM内存结构,元空间替代永久代成为方法区及常量池的变化1

    在JDK8中,JVM内存结构发生了显著变化,尤其是元空间(MetaSpace)替代了永久代(Permanent Generation)作为方法区的一部分。这种方法区的调整是由于永久代存在的一些问题,比如大小设定困难,容易引发溢出,以及给...

    JAVA面试核心知识点整理(283页).pdf

    * 方法区/永久代(Method Area/Perm Gen):方法区/永久代是所有线程共享的内存区域,用于存储类的元数据、字段和方法信息。 JVM运行时内存 JVM的运行时内存主要分为新生代和老年代两个区域。 * 新生代(Young ...

    JAVA架构知识库整理 JAVA架构知识 Java架构师 Java 架构师必备

    方法区/永久代(线程共享)2.3. JVM 运行时内存2.3.1. 新生代.2.3.1.1. Eden区2.3.1.2. ServivorFrom..2.3.1.3. ServivorTo.2.3.1.4. MinorGC 的过程(复制->清空->互换)1: eden, servicorFrom 制到 ServicorTo, 年龄+...

    java虚拟机参数配置

    PermGen Space(永久代)是Java 8及以下版本中用于存放类定义、静态变量等信息的内存区域。从Java 9开始,永久代已经被元空间(Metaspace)所取代。但在本例中,我们依然使用了针对永久代的配置参数,这些参数在Java...

    java源码包---java 源码 大量 实例

     Java编写的网页版魔方游戏,编译后生成.class文件,然后用HTML去调用,不过运行时候需要你的浏览器安装有运行Class的插件。Java源代码实现部分,比较有意思,也具参考性。像坐标控制、旋转矩阵、定时器、生成图像...

    java开源包4

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    Java 面经手册·小傅哥.pdf

    这是一本以面试题为入口讲解 Java 核心内容的技术书籍,书中内容极力的向你证实代码是对数学逻辑的具体实现。当你仔细阅读书籍时,会发现Java中有大量的数学知识,包括:扰动...4. 求职面试,总被面试题搞的死去活来的

    Java 283页技术文档整理.pdf

    永久代则存放类信息、方法信息等数据,随着Java8的发布,永久代的概念被移除,取而代之的是元数据区(Metaspace)。 在垃圾回收算法方面,文档解释了不同的算法实现,如引用计数法、可达性分析等用于确定对象是否为...

Global site tag (gtag.js) - Google Analytics