由「Metaspace容量不足触发CMS GC」从而引发的思考

转载自  由「Metaspace容量不足触发CMS GC」从而引发的思考

某天早上,毛老师在群里问「cat 上怎么看 gc」。

 

好好的一个群

看到有 GC 的问题,立马做出小鸡搓手状。


之后毛老师发来一张图。

 

老年代内存占用情况

图片展示了老年代内存占用情况。

第一个大陡坡是应用发布,老年代内存占比下降,很正常。

第二个小陡坡,老年代内存占用突然下降,应该是发生了老年代 GC。

但奇怪的是,此时老年代内存占用并不高,发生 GC 并不是正常现象。

于是,毛老师查看了 GC log。

 

GC log

从 GC log 中可以看出,老年代发生了一次 CMS GC。

但此时老年代内存使用占比 = 234011K / 2621440k ≈ 9%。

而 CMS 触发的条件是:

老年代内存使用占比达到 CMSInitiatingOccupancyFraction,默认为 92%,

毛老师设置的是 75%。

 

1-XX:CMSInitiatingOccupancyFraction = 75

于是排除老年代占用过高的可能。

接着分析内存状况。

 

Metaspace 内存占用情况

毛老师发现在老年代发生 GC 时,Metaspace 的内存占用也一起下降。

于是怀疑是 Metaspace 占用达到了设置的参数 MetaspaceSize,发生了 GC。

查看 JVM 参数设置,MetaspaceSize 参数被设置为128m。

 

1-XX:MetaspaceSize = 128m -XX:MaxMetaspaceSize = 256m

问题的原因被集中在 Metaspace 上。

毛老师查看另外一个监控工具,发生小陡坡的纵坐标的确接近 128m。

此时,引发出另一个问题:

Metaspace 发生 GC,为何会引起老年代 GC。

于是,想到之前看过 阿飞Javaer 的文章 《JVM参数MetaspaceSize的误解》。

其中有几个关键点:

Metaspace 在空间不足时,会进行扩容,并逐渐达到设置的 MetaspaceSize。

Metaspace 扩容到 -XX:MetaspaceSize 参数指定的量,就会发生 FGC。

如果配置了 -XX:MetaspaceSize,那么触发 FGC 的阈值就是配置的值。

如果 Old 区配置 CMS 垃圾回收,那么扩容引起的 FGC 也会使用 CMS 算法进行回收。

其中的关键点是:

如果老年代设置了 CMS,则 Metasapce 扩容引起的 FGC 会转变成一次 CMS。

查看毛老师配置的 JVM 参数,果然设置了 CMS GC。

 

1-XX:+UseConcMarkSweepGC

于是,解决问题的方法是调整 -XX:MetaspaceSize = 256m。

从监控来看,设置 -XX:MaxMetaspaceSize = 256m 已经足够。

因为后期并不会引发 CMS GC。


GC 的问题算是解决了,但同时引发了以下几点思考:

  1. Metaspace 分配和扩容有什么规律?

  2. JDK 1.8 中的 Metaspace 和 JDK 1.7 中的 Perm 区有什么区别?

  3. 老年代回收设置成非 CMS 时,Metaspace 占用到达 -XX:MetaspaceSize 会引发什么 GC?

  4. 如何制造 Metasapce 内存占用上升?

关于这个问题一和问题二,阿飞Javaer 已经解释的比较清楚。

对于 Metaspce,其初始大小并不等于设置的 -XX:MetaspaceSize 参数。

随着类的加载,Metaspce 会不断进行扩容,直到达到 -XX:MetaspaceSize 触发 GC。

而至于如何设置 Metaspace 的初始大小,目前的确没有办法。

在 openjdk 的 bug 列表中,找到一个 关于 Metaspace 初始大小的 bug,并且尚未解决。

 

Add JVM option to set initial Metaspace size

对于问题二, 阿飞Javaer 在文章中也进行了说明。

Perm 的话,我们通过配置 -XX:PermSize 以及 -XX:MaxPermSize 来控制这块内存的大小。

JVM 在启动的时候会根据 -XX:PermSize 初始化分配一块连续的内存块。

这样的话,如果 -XX:PermSize 设置过大,就是一种赤果果的浪费。

关于 Metaspace,JVM 还提供了其余一些设置参数。

可以通过以下命令查看。

 

1java -XX:+PrintFlagsFinal -version | grep Metaspace

关于 Metaspace 更多的内容,可以参考笨神的文章:《JVM源码分析之Metaspace解密》。

问题三

Metaspace 占用到达 -XX:MetaspaceSize 会引发什么?

已经知道,当老年代回收设置成 CMS GC 时,会触发一次 CMS GC。

那么如果不设置为 CMS GC,又会发生什么呢?

使用以下配置进行一个小尝试,然后查看 GC log。

 

1-Xmx2048m -Xms2048m -Xmn1024m 
2-XX:MetaspaceSize=40m -XX:MaxMetaspaceSize=128m
3-XX:+PrintGCDetails -XX:+PrintGCDateStamps 
4-XX:+PrintHeapAtGC -Xloggc:d:/heap_trace.txt

该配置并未设置 CMS GC,JDK 1.8 默认的老年代回收算法为 ParOldGen。

本文测试的应用在启动完成后,占用 Metaspace 空间约为 63m,可通过 jstat 命令查看。

于是,设置 -XX:MetaspaceSize = 40m,期望发生一次 GC。

从 GC log 中,可以找到以下关键日志。

 

1[GC (Metadata GC Threshold) 
2[PSYoungGen: 360403K->47455K(917504K)] 360531K->47591K(1966080K), 0.0343563 secs] 
3[Times: user=0.08 sys=0.00, real=0.04 secs] 
4
5[Full GC (Metadata GC Threshold) 
6[PSYoungGen: 47455K->0K(917504K)] 
7[ParOldGen: 136K->46676K(1048576K)] 47591K->46676K(1966080K), 
8[Metaspace: 40381K->40381K(1085440K)], 0.1712704 secs] 
9[Times: user=0.42 sys=0.02, real=0.17 secs] 

可以看出,由于 Metasapce 到达 -XX:MetaspaceSize = 40m 时候,触发了一次 YGC 和一次 Full GC。

一般而言,我们对 Full GC 的重视度比对 YGC 高很多。

所以一般都会直描述,当 Metasapce 到达 -XX:MetaspaceSize 时会触发一次 Full GC。

问题四

如何人工模拟 Metaspace 内存占用上升?

Metaspace 是 JDK 1.8 之后引入的一个区域。

有一点可以肯定的,Metaspace 会保存类的描述信息。

JVM 需要根据 Metaspace 中的信息,才能找到堆中类 java.lang.Class 所对应的对象。(有点绕)

既然 Metaspace 中会保存类描述信息,可以通过新建类来增加 Metaspace 的占用。

于是想到,使用 CGlib 动态代理,生成被代理类的子类。

简单的 SayHello 类。

public class SayHello {public void say() {System.out.println("hello everyone");}
}

简单的代理类,使用 CGlib 生成子类。

public class CglibProxy implements MethodInterceptor {public Object getProxy(Class clazz) {Enhancer enhancer = new Enhancer();// 设置需要创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);enhancer.setUseCache(false);// 通过字节码技术动态创建子类实例return enhancer.create();}// 实现MethodInterceptor接口方法public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("前置代理");// 通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.println("后置代理");return result;}
}

简单新建一个 Controller 用于测试生成 10000 个 SayHello 子类。

@RequestMapping(value = "/getProxy", method = RequestMethod.GET)
@ResponseBody
public void getProxy() {CglibProxy proxy = new CglibProxy();for (int i = 0; i < 10000; i++) {//通过生成子类的方式创建代理类SayHello proxyTmp = (SayHello) proxy.getProxy(SayHello.class);proxyTmp.say();}
}

应用启动完毕后,请求 /getProxy 接口,发现 Metaspace 空间占用上升。

 

CGlib 动态代理生成子类

从堆 Dump 中也可以发现,有很多被 CGlib 所代理的 SayHello 类对象。

 

堆 Dump 分析

代理类对应的 java.lang.Class 对象分配在堆内,类的描述信息在 Metaspace 中。

堆中有多个 Class 对象,可以推断出 Metasapce 需要装下很多类描述信息。

最后,当 Metaspace 使用空间超过设置的 -XX:MaxMetaspaceSize=128m 时,就会发生 OOM。

 

1Exception in thread "http-nio-8080-exec-6" java.lang.OutOfMemoryError: Metaspace

从 GC log 中可以看到,JVM 会在 Metaspace 占用满之后,尝试 Full GC。

但会出现以下字样。

 

1Full GC (Last ditch collection)


此外,还有一个问题。

当 Metaspace 内存占用达到 -XX:MetaspaceSize 时,Metaspace 只扩容,不会引起 Full GC。

当 Metaspace 内存占用达到 -XX:MetaspaceSize 时,会发生 Full GC。

在发生第一次 Full GC 之后,Metaspace 依然会扩容。

那么,第二次触发 Full GC 的条件是?

有文章说,在触发第一次F Full GC 后,之后 Metaspace 的每次扩容,都会引起 Full GC。

但观察本文测试的 GC log 和 jstat 命令查看 Metasapce 扩容状况,可以看出:

在第一次 Full GC 之后,之后 Metaspace 的扩容,并不一定会引起 Full GC。

 

触发一次 Full GC

从 jstat 输出可以看到,在触发一次 Full GC 之后,Metaspace 依旧发生了扩容,但未发生 Full GC。

jstat FGC 次数一直都是 1。

此外,使用 GClib 动态生成类,Metaspace 继续扩容,到一定程度,触发了 Full GC。

但触发 FGC 时,Metaspace 占比并没用明显的规律。

 

Metaspace 持续扩容再次触发 FGC

尝试了几次,由于 jstat 设置了 1s 钟输出一次,所以每次触发 Full GC 时候,MC 的数据都不一样,但基本是相同。

猜测在第一次 Full GC 之后,之后再次触发 Full GC 的阈值是有一定的计算公式的。

但具体如何计算,估计是需要深入源码了。


此外可以看到,每次 Metaspace 扩容时,都伴随着一次 YGC 或者 Full GC,不知道是否是巧合。

接着看到 占小狼 的文章 《JVM源码分析之垃圾收集的执行过程》。

文章有一句话:

从上述分析中可以发现,gc操作的入口都位于GenCollectedHeap::do_collection方法中。
不同的参数执行不同类型的gc。

打开 openjdk 8 中的 GenCollectedHeap 类,查看 do_collection 方法。

可以看到,在 do_collection 方法中,有这个一段代码。

if (complete) {// Delete metaspaces for unloaded class loaders and clean up loader_data graphClassLoaderDataGraph::purge();MetaspaceAux::verify_metrics();// Resize the metaspace capacity after full collectionsMetaspaceGC::compute_new_size();update_full_collections_completed();
}

其中最主要的是 MetaspaceGC::compute_new_size();

得出,YGC 和 Full GC 的确会重新计算 Metaspace 的大小。

至于是否进行扩容和缩容,则需要根据 compute_new_size() 方法的计算结果而定。

得出,Metasapce 扩容导致 GC 这个说法,其实是不准确的。

正确的过程是:新建类导致 Metaspace 容量不够,触发 GC,GC 完成后重新计算 Metaspace 新容量,决定是否对 Metaspace 扩容或缩容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

是现在的钱不值钱还是药太贵!

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号【雄雄的小课堂】。莫名其妙的就感觉身体不舒服&#xff0c;然后越来越严重&#xff0c;打小以来还是第一次遇见这样的&#xff0c;你说是感冒吧&#xff0c;它也不流鼻涕&#xff0c;喉咙也不痛&#xff0c;鼻子也通…

一次堆外内存泄露的排查过程

转载自 一次堆外内存泄露的排查过程 最近在做一个基于 websocket 的长连中间件&#xff0c;服务端使用实现了 socket.io 协议&#xff08;基于websocket协议&#xff0c;提供长轮询降级能力&#xff09; 的 netty-socketio 框架&#xff0c;该框架为 netty 实现&#xff0c;鉴…

.NET Core 2.0 特性介绍和使用指南

前言 这一篇会比较长&#xff0c;介绍了.NET Core 2.0新特性、工具支持及系统生态&#xff0c;现状及未来计划&#xff0c;可以作为一门技术的概述来读&#xff0c;也可以作为学习路径、提纲来用。 对于.NET Core 2.0的发布介绍&#xff0c;围绕2.0的架构体系&#xff0c;我想…

Lombok MyBatisX

Lombok的使用 [1] 什么是LomBok lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具&#xff0c;简单来说&#xff0c;比如我们新建了一个类&#xff0c;然后在其中写了几个属性&#xff0c;然后通常情况下我们需要手动去建立g…

一次堆外OOM问题的排查过程

转载自 一次堆外OOM问题的排查过程 背景 线上服务有一台机器访问不通&#xff08;一个管理平台),在公司的服务治理平台上查看服务的状况是正常的&#xff0c;说明进程还在。进程并没有完全crash掉。去线上查看机器日志&#xff0c;发现了大量的OOM异常: 017-03-15 00:00:0…

Azure与Scott Guthrie:Azure安全中心和基于角色的访问控制

InfoQ有幸采访了Microsoft执行副总裁Scott Guthrie&#xff0c;请他谈了谈Azure以及他最近的Red Shirt Dev Tours&#xff08;红杉开发之旅&#xff09;【译注1】。昨天我们谈到了Azure提供了自定义仪表盘的功能&#xff0c;它能够使得开发者创建自定义工作任务流程&#xff0c…

什么时候才能都及格呢?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注公众号【雄雄的小课堂】。今天是周五&#xff0c;又到了周测的时候了&#xff0c;发现现在考试&#xff0c;学生们的抵触情绪不会那么强烈了&#xff0c;以前只要一说啥时啥时考试&#xff0c;下面一片哀嚎声&#xff0c;各种不…

解决Visual Studio For Mac Restore失败的问题

之前就了解到微软出了mac版的VS&#xff0c;没太多的关注&#xff0c;自己也就是使用 DotNet Core SDK VS Code 做一些小demo。 前两天发布了DotNet Core 2.0 &#xff0c;Visual Studio For Mac 7.1 之后&#xff0c;感觉可以装起来用用&#xff0c;把win下面的项目转到Core…

来之不易的美团面试,结果居然挂了...(附面试答案)

转载自 来之不易的美团面试&#xff0c;结果居然挂了...&#xff08;附面试答案&#xff09; 一面 自我介绍 答&#xff1a;自我介绍是面试中唯一的自己主动介绍自己的环节&#xff0c;一定要好好把握好&#xff0c;你数据结构学的号可以手撕一个红黑树你就说我数据结构掌握…

三班的孩子们,你们现在还好吗?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注公众号【雄雄的小课堂】。三班的孩子们&#xff0c;你们还好吗&#xff1f;虽然已经就业&#xff0c;但还是会时不时的想起你们来&#xff0c;希望你们过的一切都好&#xff0c;在公司中也能快速适应。上午拿着电脑准备去四班上…

升级项目到.NET Core 2.0,在Linux上安装Docker,并成功部署

概述 容器&#xff0c;顾名思义是用来存放并容纳东西的器皿&#xff1b; 而容器技术伴着Docker的兴起也渐渐的映入大家的眼帘&#xff0c;它是一个抽象的概念&#xff0c;同时也是默默存在世上多年的技术&#xff0c;不仅能使应用程序间完全的隔离&#xff0c;而且还能在共享…

Spring Data Elasticsearch

文章目录一、 ELK二、 Elasticsearch简介三、 Linux安装Elasticsearch四、SpringData Elasticsearchpom.xmlapplication.yml创建实体创建索引 设置映射简单增删改查搜索五、 LogStash六、 使用Logback向Logstash中输出日志七、 在Kibana中查看日志信息八、 搭建日志系统九、 在…

项目参与度较低怎么办?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注公众号【雄雄的小课堂】。经过这两天做项目的表现&#xff0c;可以很明显的看的出来学生与学生之间掌握的还是有差距的&#xff0c;组内有的组员是可以为项目贡献代码的&#xff0c;但是有的组员可能只能是贡献素材&#xff0c;…

从 TFS 迁移源代码到 git

准备工具&#xff1a; https://github.com/git-tfs/git-tfs 具体的安装步骤上面的 readme.md 中有说明。通过 Chocolatey 安装&#xff0c;如果本地没有 git &#xff0c;会自动安装 git 到本地。 迁移步骤&#xff1a; 从 Visual Studio 里面进入 Source Control Explorer…

缓存穿透、缓存并发、缓存失效之思路变迁

转载自 缓存穿透、缓存并发、缓存失效之思路变迁 在用缓存的时候&#xff0c;基本上会通用遇到以下三个问题&#xff1a; 缓存穿透缓存并发缓存失效 一、缓存穿透 上面三个图会有什么问题呢&#xff1f; 我们在项目中使用缓存通常都是先检查缓存中是否存在&#xff0c;如果…

jzoj3792,P2062-分队问题【贪心】

前言 题解上说&#xff1a; 然而我的贪心不仅A了&#xff0c;而且 反例也A了 自己的洛谷题解链接&#xff1a;https://www.luogu.org/blog/user52918/solution-p2062 正题 大意 n个人&#xff0c;每个人有一个要求a[i]表示他所在的队伍里不可以少于a[i]个人&#xff0…

.net core 2.0学习笔记(二):Hello World amp;amp; 进阶

官网已经有一个.net core的入手教程&#xff08;https://www.microsoft.com/net/core#windowscmd&#xff09;&#xff0c;但这个教程完全没有顾及全宇宙第一IDE的感受。今天就跟大家体验一下在VS2017上开发.net core程序吧。VS2017开发环境的搭建请参考&#xff1a;http://www…

MyBatis】MyBatis一级缓存和二级缓存

转载自 MyBatis】MyBatis一级缓存和二级缓存 MyBatis自带的缓存有一级缓存和二级缓存 一级缓存 Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中&#xff0c;执行相同的查询SQL&#xff…

.net core 2.0学习笔记(一):开发运行环境搭建

期待已久的.net core 2.0终于发布了&#xff01;大家等的花儿都谢了。 不过比预期提前了一个多月&#xff0c;这在微软历史上还真的不多见。按照历史经验看&#xff0c;2.0版本应该比较靠谱&#xff0c;我猜这也是社区非常火爆的原因吧。下面就简单分享一下.net core2.0开发运行…

不好意思,你这个加分理由不行……

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。周五了&#xff0c;又该周测了&#xff0c;今天和以往一样&#xff0c;上午前两节课都在上课&#xff0c;第三节课进行测试&#xff0c;这周的填空题有点儿多&#xff0c;所以考试的时间较…