被废弃的持久代

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

首先我们看一张JVM内存布局的图:

注意到里面有一块METHOD AREA,它是一块线程共享的对象,名为方法区,在HotSpot虚拟机中,这块METHOD AREA我们可以认为等同于持久代(PermGen),在Java 6及之前的版本,持久代存放了以下一些内容:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

到了Java 7之后,常量池已经不在持久代之中进行分配了,而是移到了堆中,即常量池和对象共享堆内存。

接着到了Java 8之后的版本(至此篇文章,Java 10刚发布),持久代已经被永久移除,取而代之的是Metaspace。

为什么要移除持久代

HotSpot团队选择移除持久代,有内因和外因两部分,从外因来说,我们看一下JEP 122的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进行融合而做的努力,JRockit用户并不需要配置持久代(因为JRockit就没有持久代)。

从内因来说,持久代大小受到-XX:PermSize和-XX:MaxPermSize两个参数的限制,而这两个参数又受到JVM设定的内存大小限制,这就导致在使用中可能会出现持久代内存溢出的问题,因此在Java 8及之后的版本中彻底移除了持久代而使用Metaspace来进行替代。

Metaspace

上面说了,为了避免出现持久代内存溢出的问题,Java 8及之后的版本彻底移除了持久代而使用Metaspace来进行替代。

Metaspace是方法区在HotSpot中的实现,它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存。因此Metaspace具体大小理论上取决于32位/64位系统可用内存的大小,可见也不是无限制的,需要配置参数。

接着我们模拟一下Metaspace内存溢出的情况,前面说了持久代存放了以下信息:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

所以最简单的模拟Metaspace内存溢出,我们只需要无限生成类信息即可,类占据的空间总是会超过Metaspace指定的空间大小的,下面用Cglib来模拟:

 public class MetaspaceOOMTest {

     /**
* JVM参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m -XX:+PrintFlagsInitial
*/
public static void main(String[] args) {
int i = 0; try {
for (;;) {
i++; Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
} catch (Exception e) {
System.out.println("第" + i + "次时发生异常");
e.printStackTrace();
}
} static class OOMObject { } }

虚拟机参数设置为"-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=128m",运行代码,结果为:

 第15562次时发生异常
net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at org.xrq.commom.test.jvm.MetaspaceOOMTest.main(MetaspaceOOMTest.java:34)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:413)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336)
... 6 more
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
... 11 more

可见即使使用了Metaspace,也是有OOM的风险的,但是由于Metaspace使用本机内存,因此只要不要代码里面犯太低级的错误,OOM的概率基本是不存在的。

Metaspace相关JVM参数

最后我们来看一下Metaspace相关的几个JVM参数:

参数名 作  用
MetaspaceSize 初始化的Metaspace大小,控制Metaspace发生GC的阈值。GC后,动态增加或者降低MetaspaceSize,默认情况下,这个值大小根据不同的平台在12M到20M之间浮动
MaxMetaspaceSize 限制Metaspace增长上限,防止因为某些情况导致Metaspace无限使用本地内存,影响到其他程序,默认为4096M
MinMetaspaceFreeRatio 当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机增长Metaspace的大小,默认为40,即70%
MaxMetaspaceFreeRatio 当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放部分Metaspace空间,默认为70,即70%
MaxMetaspaceExpanison Metaspace增长时的最大幅度,默认值为5M
MinMetaspaceExpanison Metaspace增长时的最小幅度,默认为330KB

Java虚拟机16:Metaspace的更多相关文章

  1. Java虚拟机16:Java内存模型

    什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...

  2. 【文章阅读】Java虚拟机系列学习

    总目录: Java虚拟机 - 随笔分类 - 五月的仓颉 - 博客园 http://www.cnblogs.com/xrq730/category/731395.html 已读: Java虚拟机1:什么 ...

  3. 深入理解java虚拟机---JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)(十二)

    引用:https://www.cnblogs.com/yulei126/p/6777323.html JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)   1.背景 2.为什么废 ...

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

    一.背景 1.1 永久代(PermGen)在哪里? 根据,hotspot jvm结构如下(虚拟机栈和本地方法栈合一起了): 上图引自网络,但有个问题:方法区和heap堆都是线程共享的内存区域. 关于方 ...

  5. 《深入Java虚拟机学习笔记》- 第16章 控制流

    <深入Java虚拟机学习笔记>- 第16章 控制流

  6. 《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读

    堆内存使用分析,GC 日志解读 重要的东东 在Java中,对象实例都是在堆上创建.一些类信息,常量,静态变量等存储在方法区.堆和方法区都是线程共享的. GC机制是由JVM提供,用来清理需要清除的对象, ...

  7. 《深入理解Java虚拟机》(四)虚拟机性能监控与故障处理工具

    虚拟机性能监控与故障处理工具 详解 4.1 概述 本文参考的是周志明的 <深入理解Java虚拟机> 第四章 ,为了整理思路,简单记录一下,方便后期查阅. JDK本身提供了很多方便的JVM性 ...

  8. Java 8 的 Metaspace

    Java 8 的 Metaspace https://www.cnblogs.com/xrq730/p/8688203.html 被废弃的持久代 想起之前面试的时候有面试官问起过我一个问题:Java ...

  9. Java虚拟机 - 符号引用和直接引用理解

    java -- JVM的符号引用和直接引用 https://www.zhihu.com/question/50258991 在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号 ...

随机推荐

  1. TypeError: Error #1034: 强制转换类型失败:无法将 "0.49" 转换为 mx.graphics.IFill。

    1.错误描述 TypeError: Error #1034: 强制转换类型失败:无法将 "0.49" 转换为 mx.graphics.IFill. at mx.charts.ser ...

  2. VBA Excel WideCharToMultiByte Compile error on 64-bit System

    Compile Error: The code in this project must be updated for use on64-bit systems. Please review and ...

  3. 从零一起学Spring Boot之LayIM项目长成记(三) 数据库的简单设计和JPA的简单使用。

    前言 今天是第三篇了,上一篇简单模拟了数据,实现了LayIM页面的数据加载.那么今天呢就要用数据库的数据了.闲言少叙,书归正传,让我们开始吧. 数据库 之前有好多小伙伴问我数据库是怎么设计的.我个人用 ...

  4. python利用jieba进行中文分词去停用词

    中文分词(Chinese Word Segmentation) 指的是将一个汉字序列切分成一个一个单独的词. 分词模块jieba,它是python比较好用的分词模块.待分词的字符串可以是 unicod ...

  5. Vue-自定义事件之—— 子组件修改父组件的值

    如何利用自定义的事件,在子组件中修改父组件里边的值? 关键点记住:三个事件名字 步骤如下: 这里,相对本案例,父组件定义为Second-module,对应的子组件是Three-module 第一步:你 ...

  6. Vue-组件嵌套之——父组件向子组件传值

    父组件向子组件传值步骤: 在这里先定义一下,相对本案例来说:App.vue是父组件,Second-module.vue是子组件. 一.首先,值肯定是定义在父组件中的,供所有子组件共享.所以要在父组件的 ...

  7. 关系型数据库工作原理-时间复杂度(翻译自Coding-Geek文章)

    本文翻译自Coding-Geek文章:< How does a relational database work>. 原文链接:http://coding-geek.com/how-dat ...

  8. MySQL多数据源笔记3-分库分表理论和各种中间件

    一.使用中间件的好处 使用中间件对于主读写分离新增一个从数据库节点来说,可以不用修改代码,达到新增节点数据库而不影响到代码的修改.因为如果不用中间件,那么在代码中自己是先读写分离,如果新增节点, 你进 ...

  9. 图像实验室 website 项目日志

    day 1 1.问题: 在演示界面选择浏览本地图片,上传以后不显示上传图片 原因:PIL库没有装好,参见之前博客重装 2.问题: 可以上传图片并在网站上显示,但是不能得到运行结果的图片. 原因:没有将 ...

  10. win10环境下利用pyinstaller把python代码(.py)打包成可执行文件(.exe)

    前言 最近写了一个小小的检测程序,python写起来只需要短短一百行,可是打包起来就没有C那么容易了.下面记录一下我艰难的"打包"过程. 方法一:py2exe py2exe是一种经 ...