JVM实战(24)——大对象优化

这篇具有很好参考价值的文章主要介绍了JVM实战(24)——大对象优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

一、案例背景

本章将介绍一个因为大对象而导致的频繁GC问题,其本质也是开发童鞋在写程序代码时存在bug,进而导致出现JVM性能问题。

首先,这个系统上线之后发现一天的Full GC次数多达几十次,通常来说,我们建议的一个比较良好的JVM性能,应该是Full GC几天才发生一次,或者最多一天发生几次而已。

生产环境这个系统部署在2核4G的机器上,JVM参数如下:
-Xms=1536M -Xmx=1536M -Xmn=512M -Xss=256K -XX:SurvivorRatio=5 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=68 -XX:CMSParallelRemarkEnable -XX:UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:PrintHeapAtGC -Xloggc:gc.log

比较关键的几个设置是:

  • -XX:SurvivorRatio=5:表示Eden:Survivor:Survivor=5:1:1
  • -XX:CMSInitiatingOccupancyFraction=68:表示一旦老年代内存使用达到68%,就会触发Full GC

此时,整个系统对应的JVM内存模型如下:

JVM实战(24)——大对象优化,jvm专题,jvm

1.1 存在问题

我们通过jstat进行分析,统计出的JVM性能如下:

  • 系统运行时间:6天
  • 6天内的Full GC次数和总耗时:250次,70秒
  • 6天内的Young GC次数和总耗时:26000次,1400秒

也就是说,平均每20s触发一次Young GC,每30分钟触发一次Full GC。根据Eden区和老年代的空间可以估算,系统每秒钟会产生约20MB对象进入Eden,每30分钟会有约600MB对象进入老年代:

JVM实战(24)——大对象优化,jvm专题,jvm

根据参数-XX:CMSInitiatingOccupancyFraction=68,老年代内存使用达到68%,就会触发Full GC,一次Full GC时间约300ms。

二、大对象

2.1 优化前

通过上述的案例背景介绍,我们首先想到的是会不会因为Survivor区太小,导致Young GC后的存活对象太多,放不下Survivor了,所以就一直有对象流入老年代,进而导致30分钟后触发Full GC?

但这只是推论,因为对象进入老年代也可能是因为动态年龄判断规则,所以我们就需要通过工具在高峰期观察JVM的内存使用情况。

事实上,我们观察到每次 Young GC后进入老年代的对象非常少,而且一次Young GC的存活对象也就是几十MB,Survior区可以容纳,偶尔触发动态年龄判断规则时,才有几十MB对象进入老年代:

JVM实战(24)——大对象优化,jvm专题,jvm

因此,分析到这里就很奇怪了,因为通过jstat追踪,并不是每次Young GC后都有几十MB对象进入老年代,而是偶尔才有对象进入老年代,记住,是偶尔。

那么老年代里面到底为什么会有那么多对象呢?

我们观察发现, 系统运行一段时间后,突然间老年代中的对象就会增加五六百MB 。

答案已经很明显了—— 大对象 !一定是系统运行时,每隔一段时间就会产生几百兆的大对象,直接进入老年代,不会走年轻代的Eden区,然后配合年轻代还偶尔会有几十MB对象进入老年代,所以才30分钟触发一次Full GC:

JVM实战(24)——大对象优化,jvm专题,jvm

2.2 优化后

了解了问题的所在,我们就开始针对这个案例进行优化。首先,就是要确定这个大对象到底是什么?

我们采用jmap工具导出一份JVM内存快照,然后通过jhat或者Visual VM之类的可视化工具进行分析,发现那几百兆大对象就是从数据库中查出的记录,然后对SQL进行排查,发现某个SQL在一种特殊场景下会执行类似SELECT * FROM的语句,导致一次性从数据库中查出几十万条数据。

针对该问题,主要做以下几点优化:

  1. 解决程序bug,不允许全表查询,这样就避免了大对象直接进入老年代的问题;
  2. Survivor区明显不够,70MB的空间很容易触发动态年龄判断,所以为其分配更多空间。

优化后的JVM参数如下:

-Xms=1536M -Xmx=1536M -Xmn=1024M -Xss=256K -XX:SurvivorRatio=5 -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:CMSParallelRemarkEnable -XX:UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:PrintHeapAtGC -Xloggc:gc.log

可以看到,新生代直接分配1G空间,其中Survivor各占150MB左右,此时Young GC过后的几十MB存活对象一般就不会进入老年代了。

同时,调整参数-XX:CMSInitiatingOccupancyFraction=92,将比例提高至92%,避免老年代仅占62%就触发Full GC。

最后,还设置永久代大小为256MB,因为默认永久代就几十MB,如果程序使用了反射等机制,很容
易被占满。

经过上述优化,系统基本上每分钟一次Young GC,几天才会发生一个Full GC。

三、总结

本章,我们通过示例分析了大对象导致的频繁Full GC问题,并一步一步展现了发现问题、分析问题、解决问题的思路。当我们发现Young GC过后并不是每次都有很多存活对象进入老年代的时候,就要从别的角度考虑下到底为什么会有那么多存活对象进入老年代。文章来源地址https://www.toymoban.com/news/detail-797608.html

到了这里,关于JVM实战(24)——大对象优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM实战(23)——内存碎片优化

    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 学习必须往深处挖,挖的越深,基础越扎实! 阶段1、深入多线程 阶段2、深入多线程设计模式 阶段3、深入juc源码解析

    2024年01月18日
    浏览(49)
  • JVM实战(25)——元数据区优化

    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 学习必须往深处挖,挖的越深,基础越扎实! 阶段1、深入多线程 阶段2、深入多线程设计模式 阶段3、深入juc源码解析

    2024年01月22日
    浏览(33)
  • JVM零基础到高级实战之对象存活算法引用计数法存在的特点分析

    JVM零基础到高级实战之对象存活算法引用计数法存在的特点分析 JVM零基础到高级实战之对象存活算法引用计数法存在的特点分析 优点 引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。 缺点 无法检测出循环引用。如父对

    2024年02月15日
    浏览(94)
  • JAVA工程师面试专题-JVM篇

    目录 一、运行时数据区 1、说一下JVM的主要组成部分及其作用? 2、说一下 JVM 运行时数据区 ? 3、说一下堆栈的区别 4、成员变量、局部变量、类变量分别存储在什么地方? 5、类常量池、运行时常量池、字符串常量池有什么区别? 6、JVM为什么使用元空间替换永久代 二、垃

    2024年02月21日
    浏览(45)
  • Jvm对象回收算法-JVM(九)

    上篇文章介绍了jvm运行时候对象进入老年代的场景,以及如何避免频繁fullGC。 Jvm参数设置-JVM(八) 老年代分配担保机制 这个机制的目的是为了提升效率,在minorGC之前,会有三次判断,之后再次minorGC速度会很快。 老年代剩余空间是否 大于 年轻代里现在所有对象 大于的话则

    2024年02月13日
    浏览(52)
  • JVM-JVM中对象的结构

    对象内存布局 对象里的三个区: 对象头(Header):Java对象头占8byte。如果是数组则占12byte。因为JVM里数组size需要使用4byte存储。 标记字段MarkWord: 用于存储对象自身的运行时数据,它是synchronized实现轻量级锁和偏向锁的关键。 默认存储:对象HashCode、GC分代年龄、锁状态等

    2024年02月20日
    浏览(41)
  • Jvm创建对象之内存分配-JVM(七)

    上篇文章介绍了jvm创建,会校验是否已加载类,没有则加载,通过之前学的源码,classLoader加载完之后,虚拟机开始给类分配内存,指针移动分配和free链表分配,解决并发分配情况用cap和TLAB方法。之后设置对象头部信息,有mark word线程锁,分代年龄等,klass pointer。还有指针

    2024年02月13日
    浏览(68)
  • JVM-JVM中对象的生命周期

    申明:文章内容是本人学习极客时间课程所写,文字和图片基本来源于课程资料,在某些地方会插入一点自己的理解,未用于商业用途,侵删。 原资料地址:课程资料 对象的创建 常量池检查 :检查new指令是否能在常量池中定位到这个类的符号引用,检查类之前是否被加载过

    2024年02月20日
    浏览(42)
  • JVM系列(六) JVM 对象终结方法finalize

    我们有几个特别容易混淆的final、finally、finalize, 他们之间的区别是什么? final 是java finally 是try-catch-finally finalize 是Object 根类的方法 今天我们着重讲下 finalize方法 1.finalize方法的原理及调用 finalize方法也是Java中所有类中必有的方法,因为它是属于Object根类的方

    2023年04月08日
    浏览(37)
  • JVM---理解jvm之对象已死怎么判断?

    目录 引用计数算法 什么是引用 可达性分析算法(用的最多的) 定义:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。 但是不能解决循环引用的问题,A引用

    2024年02月12日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包