最近刚刚将自己的一个应用从CMS升级到G1,在一天早上,刚刚到办公室坐下,就收到手机一阵报警,去查看了监控,发现机器的内存出现了一个90度的涨幅,如下图所示:

在查看GC日志后,发现那个时间点附近出现了“to-space exhausted”这种日志(关于G1的日志学习,参见我之前的文章:【译】深入理解G1的GC日志(一)

在这里,我比较奇怪的是为啥to-sapce exhausted会导致整个机器的内存激增。我们JVM团队同学给我的解释是:老区不够了,这个时候会把young区所有对象不管死活都转成old区对象,所以总的内存使用量会暴增。这一个知识点,我之前学习G1的时候还真没有get到(关于G1的基本知识,参见之前的文章:可能是最全面的G1学习笔记)。

不过,我有另外一个疑问:xmx和xms相同的话堆空间应该不变,一开始就分配5g,然后加上非堆内存,那么java进程起来后就会超过5g,这是没问题的;但是这里利用空闲的内存也应该是利用堆上的空间,然后整体的内存块应该已经分配出去了,应该不会出现机器内存激增的情况。JVM团队的同学给我解释道:没有,第一次读写到了才会实际从os分配出来物理内存。

针对上面的问题,我们最终确定了下面的调优建议:

  • 这次没有发生FGC,可能是由于我前面将xmx和xms调大了导致的,这次准备将xmx和xms先调回到原来的值;
  • 加上HeapDumpAfterFullGC参数,下次再发生类似情况的时候,就会触发FGC,然后自动dump堆内存,就可以针对堆内存进行分析,看看是什么对象占用了这么多内存,然后就可以针对性优化。

关于to-space exhausted的更多总结

基于上面这个问题,我又去找了一些资料,整理如下。

《Java性能权威指南》

在这本书的123页有提到,上面这种情况属于晋升失败的情况——G1收集器完成了标记阶段,开始启动混合式垃圾收集,准备要清理老年代分区,但是老年代分区在垃圾收集器释放出足够的空间之前就已经被耗尽了。这种失败通常意味着混合式垃圾收集需要更迅速得完成垃圾收集,每次新生代垃圾收集需要处理更多的老年代分区。一般来说,一系列的to-space exhausted之后会跟着一次FGC。

在我们上面的这个例子中,是old区的使用速度超过了垃圾收集器的回收速度,因此可以考虑两种调优的思路

  • 让G1更早得启动混合式垃圾收集周期,通过调小-XX:InitiatingHeapOccupancyPercent=N这个参数,默认情况下该参数是45(PS:这个参数表示的是占用整个堆内存的比例),不过,这个参数也不能调得太小,否则会导致过多的并发收集周期和混合式垃圾收集,给应用早成过多的停顿。
  • 除了考虑增加速度,还可以考虑增加每次混合式垃圾收集收集的Old分区数量,通过调整-XX:G1MixedGCCountTarget=N参数可以控制每个混合式周期中回收的Old分区数量,该参数的默认值是8;

《Java性能调优指南》

要特别关注日志片段中的"to-space exhausted"和“Evacuation Failure”两个日志,如下图所示。可以看出,Evacuation Failure消耗了684.1ms,也就是说,这次转移失败导致了将近1s的应用暂停。

这种情况属于转移失败,这本书给出了两点建议:

  • 和《Java性能权威指南》一样,也建议调小-XX:InitiatingHeapOccupancyPercent=N这个参数的值,因为转移失败的代价比多执行一些并发标记周期高很多
  • 建议通过调整-XX:ConcGCThreads,增加用于垃圾收集的线程个数,代价是会多一些CPU的消耗;也就是会占用Java应用的CPU时间,这一点也需要权衡一下。
  • 有时候转移失败是由于survivor分区中没有足够的空间容纳新晋升的对象,如果是这种情况,还可以考虑增加-XX:G1ReservePercent的大小,在G1中这个默认值是10%。

总结

JVM参数的调优,是一个不断推导和尝试的过程,其中最重要的数据就是GC日志和Java堆内存快照,因此:(1)在JVM参数中一定要设置HeapDumpAfterFullGC和HeapDumpOnOutOfMemoryError两个参数,可以在发送FGC和OOM的时候将当时的Java堆情况记录下来,用于事后分析;(2)GC日志要单独打印到一个日志文件中,方便分析,如果不特别设置,GC日志会打印到stdout.log中,会有其他的日志混合在中间,影响问题排查。

JVM的参数调优并不是万能的,发生OOM或者FGC的时候,业务代码中也一定有不合理的地方,需要做合理的限制和优化,不能将所有的事情都交给JVM抗。


本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

JVM调优实战:G1中的to-space exhausted问题的更多相关文章

  1. 类加载机制+JVM调优实战+代码优化

    类加载机制 Java源代码经过编译器编译成字节码之后,最终都需要加载到虚拟机之后才能运行.虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直 ...

  2. JVM调优实战

      JVM调优实战 文档修订记录 版本 日期 撰写人 审核人 批准人 变更摘要 & 修订位置                                                   ...

  3. SpringBoot项目优化和Jvm调优(转)

    原文:https://blog.csdn.net/wd2014610/article/details/82182617 项目调优作为一名工程师,项目调优这事,是必须得熟练掌握的事情. 在SpringB ...

  4. SpringBoot项目优化和Jvm调优(楼主亲测,真实有效)

    项目调优 作为一名工程师,项目调优这事,是必须得熟练掌握的事情. 在SpringBoot项目中,调优主要通过配置文件和配置JVM的参数的方式进行. 在这边有一篇比较好的文章,推荐给大家! Spring ...

  5. SpringBoot项目优化和Jvm调优

    https://www.cnblogs.com/jpfss/p/9753215.html 项目调优 作为一名工程师,项目调优这事,是必须得熟练掌握的事情. 在SpringBoot项目中,调优主要通过配 ...

  6. 一次jvm调优过程

    jvm调优实战 前端时间把公司的一个分布式定时调度的系统弄上了容器云,部署在kubernetes,在容器运行的动不动就出现问题,特别容易jvm溢出,导致程序不可用,终端无法进入,日志一直在刷错误,ku ...

  7. JVM调优-Java垃圾回收之分代回收

    为什么要进行分代回收? JVM使用分代回收测试,是因为:不同的对象,生命周期是不一样的.因此不同生命周期的对象采用不同的收集方式. 可以提高垃圾回收的效率. Java程序运行过程中,会产生大量的对象, ...

  8. JVM调优浅谈

    1.数据类型 java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:它代表的值就是数值本身,而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本 ...

  9. JVM调优总结(五)-分代垃圾回收详述1

    为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...

随机推荐

  1. sublime 如何安装插件实现高效输入,如何支持markdown 语法,并实时预览

    啊,一直想鼓捣markdown的语法,但是配置什么的有点麻烦.不过用起来的话,真心顺手.无需考虑格式与语法点来点去影响效率, 用心去搬砖,用脚修bug 一.初识 Package Control 首先, ...

  2. 个人永久性免费-Excel催化剂功能第83波-遍历文件夹内文件信息特别是图像、音视频等特有信息

    在过往的功能中,有体现出在Excel上管理文件的极大优势,在文件的信息元数据中,有图片和音视频这两类特有的属性数据,此篇对过往功能的一个补充,特别增加了图片和音视频信息的遍历功能. 使用场景 在文件管 ...

  3. 个人永久性免费-Excel催化剂功能第73波-数据转换:单行多项目转多行单项目

    数据分析的前半部分数据处理.转换等工作是一个又脏又累的活,默默地干着,却又不出彩让人看到过程的艰辛和成果.如何让这个过程可以更加轻松一点,是Excel催化剂为大家所想的,今天带来一大刚需的数据转换功能 ...

  4. JVM调优之探索CMS和G1的物理内存归还机制

    前言: 公司有一个资产统计系统,使用频率很低,但是要求在使用时查询速度快,因此想到做一些缓存放在内存中,在长时间没有使用,持久化到磁盘中,并对垃圾进行回收,归还物理内存给操作系统,从而节省宝贵资源给其 ...

  5. ArcGIS API For JavaScript 开发(三)使用小部件设计页面框架

    其实上一个的鹰眼.比例尺.图例等都是小部件:这篇文章主要是页面布局设计,dojo提供了非常多的小部件,从功能的角度可以分为3大类:表单小部件.布局小部件和应用小部件. 表单小部件于HTML中的表单部件 ...

  6. 十一、SQL Server CONVERT() 函数

    定义和用法 CONVERT() 函数是把日期转换为新数据类型的通用函数. CONVERT() 函数可以用不同的格式显示日期/时间数据. 语法 CONVERT(data_type(length),dat ...

  7. C#使用CLR/C++的DLL间接调用Native C++的DLL

    C#使用CLR/C++的DLL间接调用Native C++的DLL 开发环境:win 7  VS2010 简介:C#的exe使用CLR/C++间接调用Native C++的DLL. 第一步:创建一个C ...

  8. linux初学者-系统启动故障篇

    linux初学者-系统启动故障篇 在系统的操作中,有时会不小心误删或者操作失误使得系统启动不起来,下文将列举几种常见的系统启动失败的情况及解决的办法. 1.删除或者覆盖mbr的446个字节 mbr的4 ...

  9. springcloud-provider-consumer-register

    作者:纯洁的微笑出处:http://www.ityouknow.com/ 版权归作者所有,转载请注明出处 上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注 ...

  10. 100天搞定机器学习|Day9-12 支持向量机

    机器学习100天|Day1数据预处理 100天搞定机器学习|Day2简单线性回归分析 100天搞定机器学习|Day3多元线性回归 100天搞定机器学习|Day4-6 逻辑回归 100天搞定机器学习|D ...