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

前言

文本已收录至个人GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是如今 git

我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客github

絮叨

前面的章节面试

其实前面的都只是铺垫,真正的东西来了,是骡子是马总得遛遛才知道,因此呢,你们一块儿来看看针对不一样的业务场景,分析具体的业务来调优缓存

每日上亿请求的电商系统JVM调优

背景引入

咱们的背景是电商系统,电商系统其实通常会拆分红为不少的子系统独立部署,好比商品系统,订单系统,活动系统,数据统计系统,会员系统等。app

这边就以订单系统来作例子jvm

每日上亿请求,那么它会有多少活跃用户 post

通常每一个用户平均访问20次,那么上亿大概有500W 日活跃学习

那么继续推算 500W 日活 能有多少下单呢?优化

若是是10% 大概会有50W人下单,那么天天大概是50W订单spa

若是50W订单集中在4小时的高峰期内,那么平均每秒也才几十个订单,若是是几十个订单其实并不要过多去关注JVM,基本上每秒占用一些新生代获得内存,隔好久,新生代才会满,而后一次Minor GC 内存就出来了,没有啥压力

可是秒杀,大促销的场景就不这样了

若是是双11 基本上几分钟就会有几十万的订单,那么每秒的下单量 多是上千 破万

抗住大促销须要几台机器

基本上3台机器能够了 每一个机器每秒300个下单 部署的是4核8G

从自己来讲这个硬件资源是够了 扛住每秒300个 问题不大

可是问题在于须要对JVM 有限的内存资源合理分配,让JVM GC次数减小,并且尽可能避免Full GC

大促销订单系统的内存使用模型

基本上每秒处理300个下单来估算,其实由于下单涉及的接口请求是比较耗时的,因此呢每秒处理100到300是差很少的

那么每一个订单的实体个人字段我按照1kb来算,单单300个订单就会有300kb的内存开销

而后算上一系列的其余业务对象 就是放大10倍到20倍

此外,除了下单外 还有不少跟订单相关的其余操做,好比查询这些 咱们再扩大10倍

那么每秒就是 300kb * 20 * 10=60MB的内存开销,这个内存开销会再1秒后能够是回收状态,以下图

内存如何分配

假设咱们是4核8G的机器,那么给到JVM 通常是4G,剩下的给操做系统来使用,其中堆内存咱们能够给到3G,新生代给1.5G,老年代给1.5G

而后每一个线程的Java虚拟机栈是1M,那么JVM若是有几百个线程大概是几百M

而后永生代 给256内存,基本上4G差很少了

此时JVM的示意图以下

接下来就很明确了 订单系统每秒大概300个订单 ,都是占据新生代60MB的内存,那么新生代大概是25秒中就会占满,以下图

25秒以后就会要进行Minor GC了,此时由于有-XX:HandlePromotionFailure选项,因此你能够认为须要进行的检查主要是比较 老年代可用空间大小和历代Minor GC后进入老年代对象的平均大小,刚开始确定这个检查是能够经过的
因此Minor GC直接运行,一会儿能够回收掉99%的新生代对象,由于除了 最近一秒的订单请求还在处理,大部分的订单早就处理完毕了,因此此时可能存活的对象也就是100MB

可是问题来了 若是-XX:SuruvivorRatio 参数默认为8,那么此时Eden大概占用1.2GB内存,每一个Surviuvor是150MB,以下图

而后再次运行20秒,把Eden区暂满,再次垃圾回收Eden和S1中的对象,存活对象可能仍是100MB左右 会进入S2,以下图

此时JVM参数以下

新生代垃圾回收优化之一:Survivor空间够不够

首先在进行JVM优化的时候,第一个要考虑的问题就是你经过估算,你的新生代的Survivor到底够不够

按照上面的逻辑 首先每次新生代垃圾回收是100MB ,有可能会突破150MB,那么岂不是常常出现Minor GC事后没法放到Survivor中,那么岂不是会频繁进入到老年代?

还有,即便是Minor GC后 的对象少于150MB,可是即便是100MB的对象进入了Survuvor区,由于这是一批相同年龄对象,直接超过了空间的50%,此时也可能会致使对象进入老年代

因此按这个模型来讲,Surivor区域的空间明显是不够的

其实这里建议是把新生代调整为2G 老年代1G,那么此时Eden为1.6G,每一个Survivor为200MB,以下图

这个时候,Survivor区域变大,就大大下降了新生代GC事后存活对象在Survivor里放下不下的问题,或者同龄对象超过50%的状况

这样就大大下降了 新生代进入老年代的几率

此时JVM的参数以下

其实对于任何一个系统,首先相似上面的内存使用模型预估以及合理的分配内存,尽可能让每次Minor GC后的对象都留着Survivor里,不要进入老年代,这个是你首先要优化的地方

新生代对象躲过多少次垃圾回收进入老年代

你们知道 一个对象连续躲过15次垃圾回收会自动的进入老年代

其实按照上面的内存运行模型来讲,基本上20秒触发一次Minor GC ,也就是一个对象再新生代几分钟还没回收,它就会进入老年代

有写博客说,要把参数设置高一点 20 30 ,其实这种说法是不对的

由于你对这个参数考虑必须结合系统的运行模型来讲,若是躲过了15次GC 都几分钟,若是一个对象几分钟都不回收,确定是系统里@Service这类注解

因此谁,考虑问题,必定不要人云亦云,要结合运行原理,本身推演和思考,不一样的业务系统还都是不同的

其实这个参数你也能够适当的下降它的值,好比说降到5次,也就是一个对象可以躲过5次Minor GC,在新生代超过1分钟,尽快让他进入老年代,别再新生代里面占着内存了

总之,这个参数必是结合你的系统具体运行模型来考虑的,此时JVM参数以下

多大的对象直接进入老年代?

另一个逻辑就是说,大对象能够直接进入老年代,由于大对象说明是要长期存活和使用的

好比在JVM里可能会缓存一些数据,这个通常能够结合本身系统中到底有没有建立大对象来决定的

可是通常来讲,这个给他设置1MB足够,由于不多有对象超过1MB的大对象,若是有,多是你提早分配的,大List之类的

此时JVM的参数以下

指定垃圾回收器

同时你们别忘记了要指定垃圾回收器,新生代使用ParNew,老年代使用CMS,以下JVM参数:

总结

其实你们看完这个案例,就能够直接去设置本身系统的JVM的参数了,看看你新生代的大小,老年代的大小,Eden和Survivor的大小,而后午估算一下你的系统运行模型

  • 每秒占用多少内存?
  • 多长时间触发一次Minor GC
  • 通常Minor GC 后还有多少存活对象
  • Survivor能放的下吗
  • 会不会频繁由于Survivor放不下致使对象进入老年代?
  • 会不会由于动态年龄判断规则进入老年代?

结尾

JVM实战一,这个是最基本的调优,全部的JVM调优都是根据业务估算来的

由于博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 但愿能坚持个一年吧 但愿各位大佬多提意见,让我多学习,一块儿进步。

平常求赞

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

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

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

相关文章
相关标签/搜索