为什么用元空间替代永久代?

简介: 为什么用元空间替代永久代?

永久代和元空间都是 HotSpot 虚拟机中的概念,HotSpot 虚拟机是 Sun JDK 和 Open JDK 中自带的虚拟机,也是目前使用范围最广泛的 Java 虚拟机,当我们提到虚拟机时,大概率指的就是 HotSpot 虚拟机。

但从《Java 虚拟机规范》的层面来说,并没有所谓的“永久代”和“元空间”等区域,详见官方文档:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-2.html#jvms-2.5。《Java 虚拟机规范》只是规定了一个区域叫“方法区(Method Area)”,而 “永久代”和“元空间”是 HotSpot 虚拟机在不同的 JDK 版本下,对方法区的具体实现而已。这就好像,世界羽协规定羽毛球比赛必须要使用羽毛球拍(方法区),而中国羽毛球运动员,第一年使用的是红双喜牌的羽毛球拍(永久代),第二年使用的是李宁牌羽毛球拍(元空间)一样。

那么问题来了,永久代为什么被元空间给替代了?

1.官方答案

关于这个问题,官方在 JEP 122: Remove the Permanent Generation(移除永久代)中给出了答案,原文内容如下:

Motivation(动机)
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

以上内容翻译成中文大意是:

这是 JRockit 虚拟机和 HotSpot 虚拟机融合工作的一部分。JRockit 客户不需要配置永久层代(因为 JRockit 没有永久代),所以要移除永久代。

JRockit 是 Java 官方收购的一家号称史上运行最快的 Java 虚拟机厂商,之后 Java 官方在 JDK 8 时将 JRockit 虚拟机和 HotSpot 虚拟机进行了整合。

PS:JEP 是 JDK Enhancement Proposal 的缩写,翻译成中文是 JDK 改进提案。你也可以把它理解为 JDK 的更新文档。

通过官方的描述,我们似乎找到了答案,也就是说,之所以要取消“永久代”是因为 Java 官方收购了 JRockit,之后在将 JRockit 和 HotSpot 进行整合时,因为 JRockit 中没有“永久代”,所以把永久代给移除了

PS:上面的那段描述好像说的已经很清楚了,但又好像什么也没说。这就好比,我问你“为什么要买车?”,你说“别人都买车了,所以我要买车”,但为什么别人要买车?

2.背后的原因

上述给出了移除永久代的回答,但却没有给出背后的原因,那接下来我们就来讨论一下,为什么要移除永久代?以及为什么要有元空间?

2.1 降低 OOM

当使用永久代实现方法区时,永久代的最大容量受制于 PermSize 和 MaxPermSize 参数设置的大小,而这两个参数的大小又很难确定,因为在程序运行时需要加载多少类是很难估算的,如果这两个参数设置的过小就会频繁的触发 FullGC 和导致 OOM(Out of Memory,内存溢出)。

但是,当使用元空间替代了永久代之后,出现 OOM 的几率就被大大降低了,因为元空间使用的是本地内存,这样元空间的大小就只和本地内存的大小有关了,从而大大降低了 OOM 的问题。

2.2 降低运维成本

因为元空间使用的是本地内存,这样就无需运维人员再去专门设置和调整元空间的大小了。

3.方法区发展史

在 HotSpot 虚拟机中,方法区的实现经历了以下 3 个阶段:

  1. JDK 1.6 及之前:方法区使用永久代实现,静态变量存放在永久代
  2. JDK 1.7 :“去永久代”的前置版本,还存在永久代,不过已经将字符串常量池和静态变量从永久代移到了堆上
  3. JDK 1.8 及以后:无永久代,使用元空间(存放在本地内存中)实现方法区,常量保存在元空间,但字符串常量池和静态变量依然保存在堆中

总结

永久代和元空间都是 HotSpot 虚拟机对《Java 虚拟机规范》中方法区的实现,在 JDK 1.8 之前 HotSpot 是使用永久代来实现方法区的,但这样会导致 JVM 调优比较困难,且容易发生 OOM 的问题,而 JDK 1.8 及之后,使用的是元空间存放在本地内存中的方式来替代永久代的,这样就降低了 OOM 发生的可能性,也是 JRockit 和 HotSpot 融合之后的改动之一。

参考 & 鸣谢

openjdk.org/jeps/122

本文已收录到 Gitee 开源仓库《Java 面试指南》,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。Java 面试有它就够了: 超全 Java 常见面试题,持续更新...
相关文章
|
消息中间件 持续交付 Docker
Docker与微服务:构建和部署微服务架构的完整指南
微服务架构已经成为现代应用开发的主要范式之一,而Docker容器技术则为微服务的构建、部署和管理提供了理想的解决方案。本文将深入探讨如何使用Docker构建和部署微服务架构,提供更多示例代码和细致的指南,以帮助大家更全面地理解和运用这些关键概念。
|
算法 索引 容器
如何做一个俄罗斯方块游戏(二)
嗨!大家好,我是小蚂蚁。今天我们继续学习如何做一个俄罗斯方块游戏。整个系列教程计划按照这个流程图开展,这也是我制作整个游戏的过程,今天我们就来看一下“随机生成形状”这个模块。想要实现随机的生成形状,首先必须要知道在俄罗斯方块中一共有多少种形状(也就是我们上一节中留下的第二个问题)。
676 0
|
SQL 消息中间件 JSON
微服务开发系列——第一篇:项目搭建(保姆级教程)
本节实现目标 搭建ac-mall2-cloud微服务基础骨架。 搭建微服务子项目:mall-pom、mall-common、mall-member、mall-product。 MyBatis-Plus配置:雪花ID、创建时间/修改时间 自动填充。 单个微服务子项目Swagger配置及访问。 返回JSON数据日期格式化。 Swagger优化:mall-common支持多个微服务Swagger配置、Swagger传参(语言参数、token、测试账号)
|
SQL 存储 关系型数据库
面试突击:MVCC 和间隙锁有什么区别?
面试突击:MVCC 和间隙锁有什么区别?
371 0
|
存储 缓存 前端开发
函数式编程看React Hooks(一)简单React Hooks实现
函数式编程(英语:functional programming)或称函数程序设计、泛函编程,是一种编程范式,它将计算机运算视为函数运算,并且避免使用程序状态以及易变对象。其中,λ演算(lambda calculus)为该语言最重要的基础。而且,λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
函数式编程看React Hooks(一)简单React Hooks实现
|
缓存 Java 程序员
别再问我 new 字符串创建了几个对象了!我来证明给你看!(上)
别再问我 new 字符串创建了几个对象了!我来证明给你看!
213 0
别再问我 new 字符串创建了几个对象了!我来证明给你看!(上)
|
IDE Java 程序员
99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!(上)
99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!(上)
293 0
99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!(上)
|
缓存 安全 前端开发
Android中高级面试题(理论知识)
1. ThreadLocal的理解 可以保证线程的安全。在多个线程共享相同的数据的时候,会为每个线程创建单独的副本,在单独的副本上进行数据的操作,不会对其它线程的数据产生影响,保证了线程安全。 2. HashMap HashSet HashTable的区别? 都是集合,底层都是Hash算法实现的。
1537 0
|
Python
Python全栈工程师(异常(基础))
  ParisGabriel                每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰                            Python人工智能从入门到精通         补充:包的相对导入 只对后两种导入方式有用  前两次改进的学生管...
1716 0