🔥JVM从入门到入土之实战JVM调优(二)

1,232 阅读7分钟

前言

文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…


种一棵树最好的时间是十年前,其次是现在鼓励大家在技术的路上写博客

絮叨

前面的章节

前面的章节

昨天我们做了一个基本的调优,但是你发现没有我们仅仅是说明了新生代的调优,今天我们就来看看老年代应该怎么来搞

前文回顾

上一篇文章我们已经给大家介绍了一个每日百万活跃以及亿圾请求的案例背景,同时采用电商大促期间高峰的下单场景,作为我们JVM优化分析的一个场景,推测出在大促销峰期,每秒每台机器会有300个下单

进而推测每秒会使用60MB的内存,然后根据这个背景推算出来我们一台4核8G的机器上,应该如何合理的分配内存,

进而保证可以每隔20秒一次新生代GC后的100MB左右存活对象,会进入200MB的Survivor区域内,一般不会因为Survivor赛不下或者动态年龄判断进入老年代。

同时还根据Minor GC 的频率,合理降低了大龄对象进入老年代的年龄,尽快让一些长期存活的对象赶紧进入老年代,不要停留在新生代,如下图所示

此时的JVM参数如下所示

在案例背景下什么时候进入老年代?

接下来我们分析 ,在目前优化好的背景下,一般什么情况下会让一些对象进入老年代呢?

首先第一种情况,就是-XX:Max TenuringThreshold=5这个参数会让在一两分钟内连续躲过5次GC的对象,直接进入老年代

这种对象一般是@Service @Controller之类的注解标志的那种业务逻辑组件,

这种对象一般一个系统最多就是几十MB

所以此时长期存活对象就会进入老年代中,如下图所示

此外,按照我们JVM参数,如果分配一个超过1MB的大对象,比如你说你创建一个大的数组或者一个大的List,那么这些对象会直接进入老年代。

但是这种情况假设我们的场景没有,所以忽略不计

此外还有就是Minor GC 过后可能存活的对象超过200MB 放不下进入老年区的,或者一下子占到Surviovr的50%,此时会有一些对象进入老年代

但是我们之前对新生代的优化,就是避免这种情况,但是也有可能刚好这么多,就进入了老年代了

我们可以做一个假设,大概就是这个订单系统促销期间,每隔5分钟会在GC之后有一小批对象进入老年代,大概200M左右,此时的JVM内存如下所示

大促销期间多久会触发一次Full GC

接着我们来研究一下多久会触发Full GC ,

首先我们看Full GC触发的条件

  • 没有打开 -XX:HandlePromotionFailure选型,结果老年代可用内存最多也就1G,新生代你对象总大小最多是1.8G,那么会导致每次Mino GC前一检查,都发现老年代的可用内存<新生代总对象大小,这样会导致每次Minor GC前都触发Full GC 当然在JDK 1.6之后废弃了这个参数,其实只要看下面2个条件就好了

  • 每次Minor GC之前,都检查一下 老年代可用内存空间< 历代Minor GC后升入老年代的平均大小,其实按照我们的设定背景,要很多次的Minor GC 才有可能碰巧有200M的对象进入老年代,所以历代进入老年代的平均对象的大小,基本上是很小的

  • 可能某次Minor GC后要升入老年代的对象有几百M,但是老年代的可用空间不足了

  • 设置了 -XX:CMSInitiatingOccupancyFaction参数,比如设置了92%,那么此前几个条件可能没有满足,但是刚好总内存超过了92%,也会进行Full GC。

其实在真正系统运行期间,可能会慢慢的有对象进入老年代,但是因为新生代我们优化过了内存分配,所以对象进入老年代的速度很慢的

所以很可能是在系统运行半小时到1小时之后,才会有接近1G的对象进入老年代

此时只要满足上述 2 3 4其中一个,就好触发Full GC

大家可以思考一下,我们假设再大促销期间,订单系统运行一小时之后,才触发Full GC,这种问题应该是不大的

因为这个是高峰期,如果是平时的话 几小时 才有可能一次Full GC 所以说这样的设计是可以的

老年代GC的时候会发生 Concurrent Mode Failure吗?

经过前面的推算,我们基本可知道,假设就是订单系统运行1小时后,老年代就有900MB的对象了,剩下的空间只有100MB了,此时就会触发一次Full GC,如下图

但是有一个问题,就是CMS再垃圾回收的时候,尤其是并发清理期间,系统程序是可以并发进行的,所以老年代的空闲空间只剩下100M了

然后此时系统还在不停的创建对象,万一此时触发了一个条件,有200M要进入老年代,此时会怎么样呢?

此时就会触发Concurrent Mode Failure 问题,因为老年代没有足够的内存来存放这200M对象,此时就会导致进入Stop the World,然后切换CMS为Serial Old ,直接禁止程序运行,然后单线程去进行老年代的垃圾回收,会收掉之后,再让系统继续运行,如下图

但是这种概率是很低的的,我们就不用去理会了

CMS垃圾回收之后进行内存碎片的整理的频率应该多高?

在CMS完成Full GC之后,一般需要执行内存的碎片整理,可以设置多少次Full GC 之后执行一次对内存碎片的整理,但是我们能有必要去修改这些参数吗

其实没有必要,因为通过上面的分析,在大促销的高峰期,Full GC可能也就是1小时,然后高峰期过去,可能几小时才有一次Full GC

所以保持默认的设置就可以了,每次Full GC之后整理一次内存碎片就好了,目前的JVM的参数如下

总结

其实对很多的普通的Java系统来说,只要对系统运行期间的内存的使用模型做好估计,然后分配好内存,尽量让Minor GC之后的存活对象在Survior不要去老年代,然后其余的参数不必要做过多的优化,系统基本上就不会太差

结尾

大家还是要自己多去尝试,自己去做过才有经验。

因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉

创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见

六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !