当前位置: 首页 > news >正文

Metaspace 之二--PermGen vs. Metaspace 运行时比较

PermGen vs. Metaspace 运行时比较

  为了更好地理解Metaspace内存空间的运行时行为,

  将进行以下几种场景的测试:

  1. 使用JDK1.7运行Java程序,监控并耗尽默认设定的85MB大小的PermGen内存空间。

  2. 使用JDK1.8运行Java程序,监控新Metaspace内存空间的动态增长和垃圾回收过程。

  3. 使用JDK1.8运行Java程序,模拟耗尽通过“MaxMetaspaceSize”参数设定的128MB大小的Metaspace内存空间。

首先建立了一个模拟PermGen OOM的代码

public class ClassA {
    public void method(String name) {
    }
}

上面是一个简单的ClassA,把他编译成class字节码放到D:/classes下面,测试代码中用URLClassLoader来加载此类型上面类编译成class

package java8;

import java.io.File;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class OOMTest {
    public static void main(String[] args) {  
        try{  
            //准备url  
            URL url = new File("D:/classes").toURI().toURL();  
            URL[] urls = {url};  
            //获取有关类型加载的JMX接口  
            ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();  
            //用于缓存类加载器  
            List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();  
            while(true) {  
                //加载类型并缓存类加载器实例  
                ClassLoader classLoader = new URLClassLoader(urls);  
                classLoaders.add(classLoader);  
                classLoader.loadClass("ClassA");  
                //显示数量信息(共加载过的类型数目,当前还有效的类型数目,已经被卸载的类型数目)  
                System.out.println("total: "+ loadingBean.getTotalLoadedClassCount());  
                System.out.println("active: "+ loadingBean.getLoadedClassCount());  
                System.out.println("unloaded: "+ loadingBean.getUnloadedClassCount());  
            }  
        } catch(Exception e) {  
            e.printStackTrace();  
        }  
    }
}

虚拟机器参数设置如下:-verbose -verbose:gc

设置-verbose参数是为了获取类型加载和卸载的信息

设置-verbose:gc是为了获取垃圾收集的相关信息

 

JDK 1.7 @64-bit – PermGen 耗尽测试

Java1.7的PermGen默认空间为85 MB(或者可以通过-XX:MaxPermSize=XXXm指定)

可以从上面的JVisualVM的截图看出:当加载超过6万个类之后,PermGen被耗尽。我们也能通过程序和GC的输出观察耗尽的过程。

程序输出(摘取了部分)

  1. ......  
  2. [Loaded ClassA from file:/D:/classes/]  
  3. total: 64887 
  4. active: 64887 
  5. unloaded: 
  6. [GC 245041K->213978K(536768K), 0.0597188 secs]  
  7. [Full GC 213978K->211425K(644992K), 0.6456638 secs]  
  8. [GC 211425K->211425K(656448K), 0.0086696 secs]  
  9. [Full GC 211425K->211411K(731008K), 0.6924754 secs]  
  10. [GC 211411K->211411K(726528K), 0.0088992 secs]  
  11. ...............  
  12. java.lang.OutOfMemoryError: PermGen space 

JDK 1.8 @64-bit – Metaspace大小动态调整测试

Java的Metaspace空间:不受限制 (默认)

从上面的截图可以看到,JVM Metaspace进行了动态扩展,本地内存的使用由20MB增长到646MB,以满足程序中不断增长的类数据内存占用需求。我们也能观察到JVM的垃圾回收事件—试图销毁僵死的类或类加载器对象。但是,由于我们程序的泄漏,JVM别无选择只能动态扩展Metaspace内存空间。程序加载超过10万个类,而没有出现OOM事件。

JDK 1.8 @64-bit – Metaspace 受限测试

Java的Metaspace空间:128MB(-XX:MaxMetaspaceSize=128m)

可以从上面的JVisualVM的截图看出:当加载超过2万个类之后,Metaspace被耗尽;与JDK1.7运行时非常相似。我们也能通过程序和GC的输出观察耗尽的过程。另一个有趣的现象是,保留的原生内存占用量是设定的最大大小两倍之多。这可能表明,如果可能的话,可微调元空间容量大小策略,来避免本地内存的浪费。

从Java程序的输出中看到如下异常。

  1. [Loaded ClassA from file:/D:/classes/]  
  2. total: 21393 
  3. active: 21393 
  4. unloaded: 
  5. [GC (Metadata GC Threshold) 64306K->57010K(111616K), 0.0145502 secs]  
  6. [Full GC (Metadata GC Threshold) 57010K->56810K(122368K), 0.1068084 secs]  
  7. java.lang.OutOfMemoryError: Metaspace 

在设置了MaxMetaspaceSize的情况下,该空间的内存仍然会耗尽,进而引发“java.lang.OutOfMemoryError: Metadata space”错误。因为类加载器的泄漏仍然存在,而通常Java又不希望无限制地消耗本机内存,因此设置一个类似于MaxPermSize的限制看起来也是合理的。

总结

  1. 之前不管是不是需要,JVM都会吃掉那块空间……如果设置得太小,JVM会死掉;如果设置得太大,这块内存就被JVM浪费了。理论上说,现在你完全可以不关注这个,因为JVM会在运行时自动调校为“合适的大小”;
  2. 提高Full GC的性能,在Full GC期间,Metadata到Metadata pointers之间不需要扫描了,别小看这几纳秒时间;
  3. 隐患就是如果程序存在内存泄露,像OOMTest那样,不停的扩展metaspace的空间,会导致机器的内存不足,所以还是要有必要的调试和监控。

 

总结

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

相关文章:

  • Codrops 教程:实现内容倾斜的 3D 幻灯片效果
  • Windows之权限的继承性 累加性 优先性 交叉性及四项基本原则
  • 给 Easyui Datagrid 扩展方法
  • Gson简要使用笔记
  • nsqd 源码,写入数据
  • Java基础加强总结(二)——泛型
  • eclipse fail to create java virtual machine
  • UIImageView -- 选择图片、循环播放
  • JTree/DefaultMutableTreeNode 树形结构
  • 各大银行的收发标准
  • CSS 元素透明
  • 【转】网站布局--瀑布流式布局
  • 使用Python实现Hadoop MapReduce程序
  • centos svn快速搭建
  • 一个IO的传奇一生(8) -- elevator子系统
  • JS 中的深拷贝与浅拷贝
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 0x05 Python数据分析,Anaconda八斩刀
  • js递归,无限分级树形折叠菜单
  • python3 使用 asyncio 代替线程
  • redis学习笔记(三):列表、集合、有序集合
  • 工作中总结前端开发流程--vue项目
  • 精彩代码 vue.js
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 让你的分享飞起来——极光推出社会化分享组件
  • 听说你叫Java(二)–Servlet请求
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • postgresql行列转换函数
  • ![CDATA[ ]] 是什么东东
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (27)4.8 习题课
  • (八)c52学习之旅-中断实验
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)计算机毕业设计ssm电影分享网站
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (生成器)yield与(迭代器)generator
  • (十) 初识 Docker file
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • *上位机的定义
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • .NET序列化 serializable,反序列化
  • /bin/bash^M: bad interpreter: No such file or directory
  • [ 转载 ] SharePoint 资料
  • [BT]小迪安全2023学习笔记(第15天:PHP开发-登录验证)
  • [C#]手把手教你打造Socket的TCP通讯连接(一)
  • [C/C++]数据结构 堆的详解
  • [C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)
  • [C++]——带你学习类和对象
  • [CISCN2019 华东北赛区]Web2
  • [CTSC2014]企鹅QQ
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷