范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

GC耗时高,原因竟是服务流量小?

  原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。   简介
  最近,我们系统配置了GC耗时的监控,但配置上之后,系统会偶尔出现GC耗时大于1s的报警,排查花了一些力气,故在这里分享下。  发现问题
  我们系统分多个环境部署,出现GC长耗时的是俄罗斯环境,其它环境没有这个问题,这里比较奇怪的是,俄罗斯环境是流量最低的一个环境,而且大多数GC长耗时发生在深夜。
  发现报警后,我立马查看了GC日志,如下:
  日志中出现了to-space exhausted,经过一番了解,出现这个是由于g1在做gc时,都是先复制存活对象,再回收原region,当没有空闲空间复制存活对象时,就会出现to-space exhausted,而这种GC场景代价是非常大的。
  同时,在这个gc发生之前,还发现了一些如下的日志。
  这里可以看到,系统在分配约30M+的大对象,难道是有代码频繁分配大对象导致的gc问题。  定位大对象产生位置
  jdk在分配大对象时,会调用 G1CollectedHeap::humongous_obj_allocate 方法,而使用async-profiler可以很容易知道哪里调用了这个方法,如下: # 开启收集 ./profiler.sh start --all-user -e G1CollectedHeap::humongous_obj_allocate -f ./humongous.jfr jps  # 停止收集 ./profiler.sh stop -f ./humongous.jfr jps
  将humongous.jfr文件用jmc打开,如下:
  根据调用栈我发现,这是我们的一个定时任务,它会定时调用一个接口获取配置信息并缓存它,而这个接口返回的数据量达14M之多。
  难道是这个接口导致的gc问题?但这个定时任务调用也不频繁呀,5分钟才调用一次,不至于让gc忙不过来吧!
  另一个疑问是,这个定时任务在其它环境也会运行,而且其它环境的业务流量大得多,为什么其它环境没有问题?
  至此,我也不确定是这个定时任务导致的问题,还是系统自身特点导致偶尔的高gc耗时。
  由于我们有固定的上线日,于是我打算先优化产生大对象的代码,然后在上线前的期间试着优化一下jvm gc参数。  优化产生大对象的代码
  我们使用的是httpclient调用接口,调用接口时,代码会先将接口返回数据读取成String,然后再使用jackson把String转成map对象,简化如下:  rsp = httpClient.execute(request); String result = IOUtils.toString(rsp.getEntity().getContent()); Map resultMap = JSONUtil.getMapper().readValue(result, Map.class);
  要优化它也很简单,使用jackson的readValue有一个传入InputStream的重载方法,用它来读取json数据,而不是将其加载成一个大的String对象再读,如下:  rsp = httpClient.execute(request); InputStream is = rsp.getEntity().getContent(); Map resultMap = JSONUtil.getMapper().readValue(is, Map.class);
  注:这里面map从逻辑上来说是一个大对象,但对jvm来说,它只是很多个小对象然后用指针连接起来而已,大对象一般由大数组造成,大String之所以是大对象,是因为它里面有一个很大的char[]数组。   优化GC参数
  优化完代码后,我开始研究优化jvm gc参数了,我们使用的是jdk8,垃圾收集器是g1,为了理解g1的调优参数,又简单学习了下g1的关键概念。  g1是分region收集的,但region也分年轻代与老年代。  g1的gc分 young gc 与mixed gc ,young gc用于收集年轻代region,mixed gc会收集年轻代与老年代region。 在 mixed gc 回收之前,需要先经历一个并发周期(Concurrent Cycles),用来标记各region的对象存活情况,让mixed gc 可以判断出需要回收哪些region。 并发周期分为如下4个子阶段:
  a. 初始标记(initial marking)
  b. 并发标记(concurrent marking)
  c. 重新标记(remarking)
  d. 清理(clean up)
  需要注意的是,初始标记(initial marking)这一步是借助young gc完成的。
  在g1中,young gc几乎没有什么可调参数,而mixed gc有一些,常见如下:
  参数
  作用
  -XX:InitiatingHeapOccupancyPercent
  开始mixed gc并发周期的堆占用阈值,当大于此比例,启动并发周期,默认45%
  -XX:ConcGCThreads
  在并发标记时,使用多少个并发线程。
  -XX:G1HeapRegionSize
  每个region大小,当分配对象尺寸大于region一半时,才认为这是一个大对象。
  -XX:G1MixedGCLiveThresholdPercent
  region存活比例,默认85%,当并发标记后发现region中存活对象比例小于这个值,mixed gc才会回收这个region,毕竟一个region如果都是存活的对象,那还有什么回收的必要呢。
  -XX:G1HeapWastePercent
  允许浪费的堆比例,默认5%,当可回收的内存比例大于此值时,mixed gc才会去执行回收,毕竟没什么可回收的对象时,还有什么回收的必要呢。
  -XX:G1MixedGCCountTarget
  mixed gc执行的次数,一旦并发标记周期确认了回收哪些region后,mixed gc就进行回收,但mixed gc会分少量多次来回收这些region,默认8次。
  -XX:G1OldCSetRegionThresholdPercent
  每次mixed gc回收old region的比例,默认10%,如果G1MixedGCCountTarget是8的话,mixed gc整体能回收80%。
  -XX:G1ReservePercent
  堆保留空间比例,默认10%,这部分空间g1会保留下来,用来在gc时复制存活对象。
  出现to-space exhausted会不会是mixed gc太慢了呢?于是我调整了如下参数:  让并发标记周期启动更早,运行得更快,将 -XX:InitiatingHeapOccupancyPercent 从默认值45%调整到35%,-XX:ConcGCThreads 从1调整为3。 -XX:G1MixedGCLiveThresholdPercent 与-XX:G1HeapWastePercent 确定回收哪些region,有多少比例垃圾才执行回收,我觉得它们的值本来就蛮激进,就没有调整。 -XX:G1MixedGCCountTarget 与-XX:G1OldCSetRegionThresholdPercent 控制mixed gc执行多少次,每次回收多少region,我将-XX:G1OldCSetRegionThresholdPercent 从10%调整到了15%,让它一次多回收些old region。 增加保留空间,避免复制存活对象失败,将 -XX:G1ReservePercent 从10%调整到20%。 尽量避免产生大对象,将 -XX:G1HeapRegionSize 增加到16m。
  于是我按照上面调整了jvm参数,可是第二天我发现还是有GC长耗时,次数也没有减少,看来问题根因和我调整的参数没有关系。  问题根因
  就这样,到了上线日,于是我上线了前面优化大对象的代码,一天后,我发现偶尔的GC长耗时竟然没有了!
  问题就这样解决了!!!
  然而我心里还是有一个大大的问号,其它环境同样有这个定时任务,一样的运行频率,jvm配置也全是一样的,为啥其它环境没有问题呢?其它环境业务流量还大一些!
  为此,我搜索了大量关于g1大对象的文章,我发现大对象的分配与回收过程有点特殊,如下:  大于region size一半的对象是大对象,会直接分配在old region,且gc时大对象不会被复制或移动,而是直接回收。  大对象回收发生在2个地方,一个是并发周期的clean up子阶段,另一个是young gc(这个特性在jdk8u60才加入)。
  我忽然想到,莫非是俄罗斯环境流量太低,触发不了young gc,且并发周期又因为什么原因没执行,但定时任务又慢慢生成大对象将old region占满,导致了to-space exhausted?
  于是,我打算写段代码试验一下,慢慢的只生成大对象,看g1会不会回收,如下:
  这个一个命令行交互程序,使用如下jvm参数启动它:  # 1600m的堆,16m的region size # rlwrap作用是使得这个命令行程序更好用 rlwrap java -server -Xms1600m -Xmx1600m -Xss1m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -Xloggc:/home/work/logs/applogs/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/work/logs/applogs/ -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseG1GC -XX:G1LogLevel=finest -XX:G1HeapRegionSize=16m -XX:MaxGCPauseMillis=200 -jar command-cli.jar
  使用了1600M的堆,16M的region size,所以总共有100个region,jdk版本是 1.8.0_222 。
  通过在这个交互程序中输入gc 9437184 20 0 ,可以生成20个9M的大对象。
  当我输入3次 gc 9437184 20 0 后,如下:
  我从gc日志中发现了一次由initial marking触发的young gc,说明g1启动了并发周期,之所以发生young gc,是因为initial marking是借助ygc执行的。
  紧接着,我发现了并发标记、重新标记与清理阶段的日志。
  然后我在jstat中发现老年代使用率降低了,因为young gc会回收大对象,所以老年代使用率降低也是正常的。
  当我又执行了2次 gc 9437184 20 0 后,使得堆占用率再次大于45%,我发现gc日志中出现如下内容:
  看字面意思是,由于mixed gc正在执行,没有再次启动并发周期。
  可是,我在这种状态下等了好久,也没有看到mixed gc的日志出来,不是说mixed gc正在执行嚒,它一定是有什么问题!
  于是,我又执行了好几次 gc 9437184 20 0 ,我在gc日志中发现了to-space exhausted!
  从上面do not start mixed GCs, reason: candidate old regions not available的日志中看到,mixed gc日志之所以长时间没出来,是因为没有可回收的region导致mixed gc没有执行,因为我们只创建了大对象,但mixed gc不回收大对象分区,所以确实是没有可回收的region的。  从 Humongous Reclaimed: 98 可以发现,这次young gc,回收了98个大对象分区,而我们总共只有100个分区,说明在inital marking之后创建的大对象,确实一直都没有回收。
  注:添加  -XX:G1LogLevel=finest  参数,才能输出Humongous Reclaimed这一项。
  但是,大对象分区占了98个,堆占用率肯定超过了45%,为何一直没有再次启动并发周期呢?
  感觉这可能是jdk的bug,于是我分别下了最新的jdk8u与jdk11u验证,发现问题在最新的jdk8u中依然存在,而jdk11u中则不存在,这应该就是JDK的Bug!
  于是我通过二分搜索法多次编译了不同版本的JDK,最终确定问题fix在jdk9_b93与jdk9_b94之间。
  并在jdk的bug库中,搜索到了相同描述的bug反馈,如下:
  https://bugs.openjdk.org/browse/JDK-8140689
  Bug改动代码如下:
  大致瞄了下代码,可能理解得不完全正确,改动逻辑如下:  G1再次启动并发周期之前,至少需要执行过一次 mixed gc 或young gc ,类似于Concurrent Cycles  -> mixed gc|young gc  -> Concurrent Cycles  -> mixed gc|young gc 。 我们的场景是,在并发周期结束之后,由于没有需要回收的分区,导致mixed gc实际没有执行,可是我们只分配了大对象,且大对象又只分配在old region,所以young gc也不可能被触发,而由于上面的条件, Concurrent Cycles 也不会被触发,因此最终大对象将堆占满触发了to-space exhausted。 修复逻辑是,当并发周期结束后,没有需要回收的分区时,should_continue_with_reclaim=false,进而使得允许不执行纯young gc而直接启动并发周期,类似于 Concurrent Cycles  -> Concurrent Cycles 。
  所以在使用JDK8时,像那种系统流量很小,新生代较大,又有定时任务不断产生大对象的场景,堆几乎必然会被慢慢占满,要解决这个问题,可参考如下处理:  优化代码,避免分配大对象。  代码无法优化时,可考虑升级jdk11。  无法升级jdk11时,可考虑减小 -XX:G1MaxNewSizePercent 让新生代小一点,让young gc能执行得更频繁些,同时老年代更大,能缓冲更多大对象分配。
  考虑到我们负责的其它系统中,时不时就有一波大响应体的请求,也没法快速修改代码,于是我统一将 -XX:G1MaxNewSizePercent 减小到30%,经过观察,修改后GC频率有所增加,但暂停时间有所下降,这是符合期望的。 总结
  当我在jdk的bug库中搜索问题时,发现不少和G1大对象相关的优化,早期JDK(如JDK8)的G1实现可能在大对象回收上不太完善,所以写代码时要注意尽量少创建大对象,以回避这些隐性问题。
  注:这之后又遇到了Update RS (ms)耗时高,竟也和大对象有关,添加-XX:-ReduceInitialCardMarks后解决,看来大对象是万恶之源啊

与美术馆合二为一,武汉远城区的这座图书馆,文艺青年的聚集地读书时期,每所学校都会有图书馆,而进入社会后,这个角色被书店替代。常年生活在湖北武汉,发现这座城市里的书店数量非常多,特别是最近几年,开放艺术和小清新的规模书店不约而同出现,成为一走进故宫御花园(1)头条创作挑战赛我们今日介绍御花园。天一门御花园位于坤宁宫后方,明代称为宫后苑,与明代紫禁城宫殿同时建成,清代改称御花园以后多次增修,现仍保留初建时的基本格局。御花园平面图全园南北纵重庆最美的景区游客中心,空中俯瞰如同一把小提琴,周边秋色渐美跟着武陵盟主灵灵游酉阳,感受最炫武陵风,我们游览了著名的桃花源景区酉州古城龚滩古镇叠石花谷中国金丝楠木园等景区,在活动结束之时,渝帆准备驾车上包茂高速离开酉阳,没想到在即将上高速的走心!为你准备了假期宅家攻略国庆假期来啦继国庆在京遛娃攻略(点击蓝字查看)我们又总结了假期宅家攻略家长朋友们在这个假期不仅能带孩子感受秋高气爽也要给予他们温馨的陪伴哦目录您将看到以下精彩内容1hr居家运动的那寿阳县方山森林公园再现昔日胜景漫步方山国家森林公园新修的旅游公路,两侧林木郁郁葱葱,各色山花点缀林间,微风轻拂,花香萦绕。这一景象得益于方山提质创A工程建设。为盘活优质旅游资源,今年以来,寿阳县深入实施方山国家灵动岛是真创意还是遮羞布?iPhone14Pro是否符合你的期待?朋友们,朋友们,有钱了哈,终于有钱了,我说的是我的朋友终于有钱了,买了最新的iPhone14Pro,本着能白嫖绝不买的原则,我借来蹭个热度,简单闲聊一下这次的iPhone14Pro羊了个羊最强通关攻略1。本攻略适用于羊了个羊所有关卡2。有攻略也不是百分之百可以过关,运气很重要3。本攻略为您指明了一条过关的方向4。想要过关需要攻略运气一定的游戏次数好了,网上看了很多羊了个羊攻略,首次在太空过国庆!神十四航天员在轨拍摄星空大片今年国庆佳节,中国航天员首次在太空度过。中国载人航天工程办公室官方发布神舟十四号乘组航天员在轨拍摄的摄影作品。正在中国空间站执行任务的神舟十四号乘组向国旗敬礼陈冬我在天地之间翱翔,舟山金秋大片来了!让人看了直流口水秋风起,晒鲞忙近日,在普陀展茅鱿鱼市场一部金秋大片正在上演浙江富丹旅游食品有限公司的晾晒区工人们正借助好天气将鱿鱼整齐地晾晒在晒架上经过几天晾晒烘干处理后这些鱿鱼鲞将成品打包销往各国庆节同学聚会主持词国庆节同学聚会主持词1尊敬的老师亲爱的同学大家好!少小离家老大回,乡音无改鬓毛衰。儿童相见不相识,笑问客从何处来。带着久别重逢的渴望,带着对老师恩重如山的敬意,带着对同学的牵挂,带沉浸式消费经营场所数量暴增400但新宠背后的问题依旧需要直面十一假期,一大批90后00后选择不出远门,而是约上三五好友,体验各种沉浸式娱乐活动。就在10月4日,一则热榜话题也备受大众关注以剧本杀密室逃脱为代表的沉浸式剧本娱乐,已成文旅消费增
小可莉是如何养成的相信很多原神玩家都会养个女儿小可莉,上期池子放弃万叶,非她不抽,可惜武器池歪了,心痛的嘞相信很多玩家不是因为可莉这个角色的强度而抽取的可莉,而是被她萌萌哒的外表吸引了。大家是不是也天天洗澡,为什么还搓出泥?搓出的泥越多越好?睡前洗个舒舒服服的热水澡是一件惬意的事但有流言说不正确的洗澡方式可能有害健康会引发猝死!这到底是怎么回事?1洗澡为什么会要命?其实,不论是先洗哪儿,对身体的影响都差不多。只是不同的为啥腐肉煮熟后不能食用?众所周知,腐肉是不能吃的,这是因为腐肉里面含有大量细菌,会危害人体健康。不过值得庆幸的是,我们人类会使用火,刚好可以帮助我们消灭食物里的细菌,但即便如此,我们还是不会食用煮熟的腐肉带状疱疹,腰间长疮不用怕,1味药搞定,不用吃,一敷就好最近临床上有好几位患者都是因为带状疱疹,腰间长疮,刚开始只是简单的几个疱疹,微疼,然后就是越来越多,疼痛难忍。今天我就给大家来讲一下,这种腰间长的疱疹是怎么来的,并分享一味中药,不普通人对喝茶,最常见的五个误解茶,是大众化的消费,也是中国人最常提及的饮品。不过,不要以为人们都对茶的了解就特别全面,其实还有不少误解。很多人依然是知道茶好,但不敢喝或不会喝。通过大数据分析,帮大家梳理了一下最老年人预防脑梗,补充叶酸很重要,如何补才正确?总结3个要点刘大爷最近感到有点头晕胸闷,前天去医院做了个检查,医生说没大事儿,就是上了年纪,有点高血压,开了点处方,就让刘大爷自己去药房买点药。可药一到手,刘大爷不乐意了,因为他看见里面有一瓶CBA苏州广东两连胜!上海四川连败不止!山东10592逆转新疆昨日CBA常规赛第六轮五场比赛相继开打,结果几家欢乐几家愁!最值得兴奋的是米勒时刻重现!一个身披23号球衣的菜鸟,让我们梦回NBA元年!青岛国信水产8087苏州肯帝亚上轮惜败吉林的名家提点曼城多特蒙德或默契言和欧冠小组赛已经打完四轮比赛了,提前出线的球队已经不在少数,这其中就包括瓜迪奥拉的曼城。蓝月亮以10分排名小组第一,但尚未锁定第一名的资格,本场做客对阵7分的多特蒙德,曼城只需要一场勇士胜国王仍需批评维金斯续约太值普尔库明加请好好反思大家好,我是183蒙面男子。今天一场西部焦点之战,勇士队主场以130125击败国王队,送上关于这次比赛的5句话球评。1。库里今天出场31分钟拿到33分,第2节在7分钟之内命中5个三乾隆帝多爱富察皇后?余生所有的时光都用来怀念祭奠她这是乾隆帝在如懿传中,第一次露出家暴的一面,啪啪两巴掌不留情面地,呼在了他最看重的两个儿子的脸上,而他所有狂躁失态的背后,都离不开那个他最爱的女人孝贤皇后富察氏。富察皇后乾隆帝一生李春江拒绝下课!上海男篮大调整,李秋平出手相助,外援一个不留就在上海男篮输给了南京同曦之后,有媒体人写道,上一个这么强阵容还连续输球的球队,那时候主教练下课了,随后南下。当时的球队是辽宁男篮,郭士强主动辞职,随后加盟了广州龙狮。如今李春江也