出自:http://1t.click/7TJ

目录:

案例背景引入

特殊的电商大促场景

抗住大促的瞬时压力需要几台机器?

大促高峰期订单系统的内存使用模型估算

内存到底该如何分配?

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

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

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

别忘了指定垃圾回收器

今日思考题

1、案例背景引入

按照惯例,我们接下来会用案例驱动来带着大家分析到底该如何在特定场景下,预估系统的内存使用模型。

然后合理优化新生代、老年代、Eden和Survivor各个区域的内存大小。

接着再尽量优化参数避免新生代的对象进入老年代,尽量让对象留在新生代里被回收掉。

我们这里的背景是电商系统,电商系统其实一般会拆分为很多的子系统独立部署

比如商品系统、订单系统、促销系统、库存系统、仓储系统、会员系统,等等

我们这里就以比较核心的订单系统作为例子来说明。

(提示:食用本案例之前,请务必充分理解专栏之前两周的文章!)

我们的案例背景是每日上亿请求量的电商系统,那么大家可以来推算一下每日上亿请求量的电商系统,他会每日有多少活跃用户?

一般按每个用户平均访问20次来计算,那么上亿请求量,大致需要有500万日活用户。

那么继续来推算一下,这500万的日活用户都是会进来进行大量的浏览,那么多少人会下订单?

这里可以按照10%的付费转化率来计算,每天大概有50万人会下订单,那么大致就是每天会有50万订单。

这50万订单算他集中在每天4小时的高峰期内,那么其实平均下来每秒钟大概也就几十个订单,大家是不是觉得根本没啥可说的?

因为几十个订单的压力下,根本就不需要对JVM多关注,基本上就是每秒钟占用一些新生代内存,隔很久新生代才会满。然后一次Minor GC后垃圾对象清理掉,内存就空出来了,几乎无压力。

2、特殊的电商大促场景

但是如果你要是考虑到特殊的电商大促场景,就不会这么想了

因为很多中小型的电商平台,确实平时系统压力其实没那么大,也没太大的高并发,每秒几千并发压力就算是高峰压力了。

但是如果遇到一些大促场景,比如双11什么的,情况就不同了。

假设在类似双11的节日里,零点的时候,很多人等着大促开始就要剁手购物,这个时候,可能在大促开始的短短10分钟内,瞬间就会有50万订单。

那么此时每秒就会有接近1000的下单请求,我们就针对这种大促场景来对订单系统的内存使用模型分析一下。

3、抗住大促的瞬时压力需要几台机器?

那么要抗住大促期间的瞬时下单压力,订单系统需要部署几台机器呢?

基本上可以按3台来算,就是每台机器每秒需要抗300个下单请求。这个也是非常合理的,而且需要假设订单系统部署的就是最普通的标配4核8G机器。

从机器本身的CPU资源和内存资源角度,抗住每秒300个下单请求是没问题的。

但是问题就在于需要对JVM有限的内存资源进行合理的分配和优化,包括对垃圾回收进行合理的优化,让JVM的GC次数尽可能最少,而且尽量避免Full GC,这样可以尽可能减少JVM的GC对高峰期的系统新更难的影响。

4、大促高峰期订单系统的内存使用模型估算

背景已经全部说完了,接下来咱们就得来预估订单系统的内存使用模型了.

基本上可以按照每秒钟处理300个下单请求来估算,其实无论是订单处理性能还是并发情况,都跟生产很接近

因为处理下单请求是比较耗时的,涉及很多接口的调用,基本上每秒处理100~300个下单请求是差不多的。

那么每个订单咱们就按1kb的大小来估算,单单是300个订单就会有300kb的内存开销

然后算上订单对象连带的订单条目对象、库存、促销、优惠券等等一系列的其他业务对象,一般需要对单个对象开销放大10倍~20倍。

此外,除了下单之外,这个订单系统还会有很多订单相关的其他操作,比如订单查询之类的,所以连带算起来,可以往大了估算,再扩大10倍的量。

那么每秒钟会有大概300kb * 20 * 10 = 60mb的内存开销。

但是一秒过后,可以认为这60mb的对象就是垃圾了,因为300个订单处理完了,所有相关对象都失去了引用,可以回收的状态。

大家看下图:

5、内存到底该如何分配?

假设我们有4核8G的机器,那么给JVM的内存一般会到4G,剩下几个G会留点空余给操作系统之类的来使用

不要想着把机器内存一下子都耗尽,其中堆内存我们可以给3G,新生代我们可以给到1.5G,老年代也是1.5G。

然后每个线程的Java虚拟机栈有1M,那么JVM里如果有几百个线程大概会有几百M

然后再给永久代256M内存,基本上这4G内存就差不多了。

同时还要记得设置一些必要的参数,比如说打开“-XX:HandlePromotionFailure”选项(不熟悉这个参数的,可以回头复习一下专栏之前的文章)

JVM参数如下所示:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:HandlePromotionFailure”

但是“-XX:HandlePromotionFailure”参数在JDK 1.6以后就被废弃了,所以现在一般都不会在生产环境里设置这个参数了。

在JDK 1.6以后,只要判断“老年代可用空间”> “新生代对象总和”,或者“老年代可用空间”> “历次Minor GC升入老年代对象的平均大小”

上述两个条件满足一个,就可以直接进行Minor GC,不需要提前触发Full GC了。

所以实际上,如果大家用的是JDK 1.7或者JDK 1.8,那么JVM参数就保持如下即可,后面也都不再加入这个参数了:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M”

此时JVM内存入下图所示。

接着就很明确了,订单系统的系统程序在大促期间不停的运行,每秒处理300个订单,都会占据新生代60MB的内存空间

但是1秒过后这60MB对象都会变成垃圾,那么新生代1.5G的内存空间大概需要25秒就会占满,如下图。

25秒过后就会要进行Minor GC了,此时因为有“-XX:HandlePromotionFailure”选项,所以你可以认为需要进行的检查,主要就是比较 “老年代可用空间大小”和“历次Minor GC后进入老年代对象的平均大小”,刚开始肯定这个检查是可以通过的。

所以Minor GC直接运行,一下子可以回收掉99%的新生代对象,因为除了最近一秒的订单请求还在处理,大部分订单早就处理完了,所以此时可能存活对象就100MB左右。

但是这里问题来了,如果“-XX:SurvivorRatio”参数默认值为8,那么此时新生代里Eden区大概占据了1.2GB内存,每个Survivor区是150MB的内存,如下图。

所以Eden区1.2GB满了就要进行Minor GC了,因此大概只需要20秒,就会把Eden区塞满,就要进行Minor GC了。

然后GC后存活对象在100MB左右,会放入S1区域内。如下图。

然后再次运行20秒,把Eden区占满,再次垃圾回收Eden和S1中的对象,存活对象可能还是在100MB左右会进入S2区,如下图。

此时JVM参数如下:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8”

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

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

按照上述逻辑,首先每次新生代垃圾回收在100MB左右,有可能会突破150MB,那么岂不是经常会出现Minor GC过后的对象无法放入Survivor中?然后岂不是频繁会让对象进入老年代?

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

(关于jvm的垃圾回收规则,如果不太清楚,请参加专栏之前的文章)

所以其实按照我们这个模型来说,Survivor区域是明显不足的。

这里其实建议的是调整新生代和老年代的大小,因为这种普通业务系统,明显大部分对象都是短生存周期的,根本不应该频繁进入老年代,也没必要给老年代维持过大的内存空间,首先得先让对象尽量留在新生代里。

所以此时可以考虑把新生代调整为2G,老年代为1G,那么此时Eden为1.6G,每个Survivor为200MB,如下图。

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

这样就大大降低了新生代对象进入老年代的概率。

此时JVM的参数如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8”

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

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

大家都知道,除了Minor GC后对象无法放入Survivor会导致一批对象进入老年代之外,还有就是有些对象连续躲过15次垃圾回收后会自动升入老年代。

其实按照上述内存运行模型,基本上20多秒触发一次Minor GC,那么如果按照“-XX:MaxTenuringThreshold”参数的默认值15次来说,你要是连续躲过15次GC,就是一个对象在新生代停留超过了几分钟了,此时他进入老年代也是应该的。

有些博客会说,应该提高这个参数,比如增加到20次,或者30次,其实那种说法根本是不对的

因为你对这个参数考虑必须结合系统的运行模型来说,如果躲过15次GC都几分钟了,一个对象几分钟都不能被回收,说明肯定是系统里类似用@Service、@Controller之类的注解标注的那种需要长期存活的核心业务逻辑组件。

那么他就应该进入老年代,何况这种对象一般很少,一个系统累计起来最多也就几十MB而已。

所以你说你提高“-XX:MaxTenuringThreshold”参数的值,有啥用呢?让这些对象在新生代里多停留几分钟?

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

其实这个参数甚至你都可以降低他的值,比如降低到5次,也就是说一个对象如果躲过5次Minor GC,在新生代里停留超过1分钟了,尽快就让他进入老年代,别在新生代里占着内存了。

总之,对于这个参数务必是结合你的系统具体运行的模型来考虑。

要记住,JVM没有万能的最佳参数,但是有一套通用的分析和优化的方法。

此时JVM参数如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5”

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

另外有一个逻辑是说,大对象可以直接进入老年代 ,因为大对象说明是要长期存活和使用的

比如在JVM里可能会缓存一些数据,这个一般可以结合自己系统中到底有没有创建大对象来决定。

但是一般来说,给他设置个1MB足以,因为一般很少有超过1MB的大对象。如果有,可能是你提前分配了一个大数组、大List之类的东西用来放缓存的数据。

此时JVM参数如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M”

9、别忘了指定垃圾回收器

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

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC”

ParNew垃圾回收器的核心参数,其实就是配套的新生代内存大小、Eden和Survivor的比例

只要你设置合理,避免Minor GC后对象放不下Survivor进入老年代,或者是动态年龄判定之后进入老年代,给新生代里的Survivor充足的空间,那么Minor GC一般就没什么问题。

然后根据你的系统运行模型,合理设置“-XX:MaxTenuringThreshold”,让那些长期存活的对象,抓紧尽快进入老年代,别在新生代里一直待着。

这样基本上一个初步的优化好的JVM参数就结合你的业务出来了。明天我们继续结合案例来分析 老年代的垃圾回收和参数优化方式。

10、今日思考题

大家看完这个案例,可以直接去看看自己生产系统的JVM参数了,看看你的新生代、老年代、Eden和Survivor的大小

然后去估算一下你的系统运行模型:

每秒占用多少内存?

多长时间触发一次Minor GC?

一般Minor GC后有多少存活对象?

Survivor能放的下吗?

会不会频繁因为Survivor放不下导致对象进入老年代?

会不会因动态年龄判断规则进入老年代?

请大家把自己的思考发至讨论区进行互动讨论

觉得文章不错的欢迎关注我的WX公众号:程序员乔戈里
我是百度后台开发工程师,哈工大计算机本硕,专注分享技术干货/编程资源/求职面试/成长感悟等,关注送5000G编程资源和自己整理的一份帮助不少人拿下java的offer的面经附答案,免费下载CSDN资源。

案例实战:每日上亿请求量的电商系统,JVM年轻代垃圾回收参数如何优化?的更多相关文章

  1. 每日上亿请求量的电商系统,JVM年轻代垃圾回收参数如何优化? ----实战教会你如何配置

    目录: 案例背景引入 特殊的电商大促场景 抗住大促的瞬时压力需要几台机器? 大促高峰期订单系统的内存使用模型估算 内存到底该如何分配? 新生代垃圾回收优化之一:Survivor空间够不够 新生代对象躲 ...

  2. 12. 亿级流量电商系统JVM模型参数二次优化

    亿级流量电商系统JVM模型参数预估方案,在原来的基础上采用ParNew+CMS垃圾收集器 一.亿级流量分析及jvm参数设置 1. 需求分析 大促在即,拥有亿级流量的电商平台开发了一个订单系统,我们应该 ...

  3. 9.亿级流量电商系统JVM模型参数预估方案

    1. 需求分析 大促在即,拥有亿级流量的电商平台开发了一个订单系统,我们应该如何来预估其并发量?如何根据并发量来合理配置JVM参数呢? 假设,现在有一个场景,一个电商平台,比如京东,需要承担每天上亿的 ...

  4. 手把手教你使用VUE+SpringMVC+Spring+Mybatis+Maven构建属于你自己的电商系统之vue后台前端框架搭建——猿实战01

            猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是 ...

  5. Java进阶专题(十三) 从电商系统角度研究多线程(上)

    前言 ​ 本章节主要分享下,多线程并发在电商系统下的应用.主要从以下几个方面深入:线程相关的基础理论和工具.多线程程序下的性能调优和电商场景下多线程的使用. 多线程J·U·C 线程池 概念 回顾线程创 ...

  6. SpringCloud Alibaba实战(2:电商系统业务分析)

    选用了很常见的电商业务来进行SpringCloud Alibaba的实战. 当然,因为仅仅是为了学习SpringCloud Alibaba,所以对业务进行了大幅度简化,这里只取一个精简版的用户下单业务 ...

  7. 数据中台实战(一):以B2B电商亿订为例,谈谈产品经理视角下的数据埋点

    本文以B2B电商产品“亿订”为实例,与大家一同谈谈数据中台的数据埋点. 笔者所在公司为富力环球商品贸易港,是富力集团旗下汇聚原创设计师品牌及时尚买手/采购商两大社群,通过亿订B2B电商.RFSHOWR ...

  8. 重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 场地和场景的重要性 射击

  9. 重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 黎明前的坚守,的住吗? 有人举过这样一个例子,先给你张北大的录 ...

随机推荐

  1. 简单的 自动生成 二维码 PHP 方法

    方法一:<style type="text/css">.eweima{    width:200px; height:200px; margin:auto;}</ ...

  2. 每日一问:谈谈 SharedPreferences 的 apply() 和 commit()

    SharedPreferences 应该是任何一名 Android 初学者都知道的存储类了,它轻量,适合用于保存软件配置等参数.以键值对的 XML 文件形式存储在本地,程序卸载后也会一并清除,不会残留 ...

  3. 个人博客小案例(纯Django搭建)

    在看这篇文章的时候,必须有django基础,如果没有点击访问 一.环境配置 新建项目并做配置项目创建,创建APP并注册 创建模板并配置相应的路径,点击下载模板,配置方法点击访问 创建静态文件并配置,点 ...

  4. 玲珑OJ 1082:XJT Loves Boggle(爆搜)

    http://www.ifrog.cc/acm/problem/1082 题意:给出的单词要在3*3矩阵里面相邻连续(相邻包括对角),如果不行就输出0,如果可行就输出对应长度的分数. 思路:爆搜,但是 ...

  5. Easy doesn't enter into grown-up life

    Hi xinlu, good afternoon, my name is xx, you can also call me andrew. i'm 31 years old, and i'm from ...

  6. Python C3 算法 手动计算顺序

    Python C3 算法 手动计算顺序   手动计算类继承C3算法原则: 以所求类的直接子类的数目分成相应部分 按照从左往右的顺序依次写出继承关系 继承关系第一个第一位,在所有后面关系都是第一个出现的 ...

  7. 串门赛: NOIP2016模拟赛——By Marvolo 丢脸记

    前几天liu_runda来机房颓废,顺便扔给我们一个网址,说这上面有模拟赛,让我们感兴趣的去打一打.一开始还是没打算去看一下的,但是听std说好多人都打,想了一下,还是打一打吧,打着玩,然后就丢脸了. ...

  8. Scala 函数式编程(一) 什么是函数式编程?

    为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用 scala 给你讲述函数式编程中的优势,以及一些函数式的哲学.不懂 scala 也没关系,scala 和 java 是类似的 ...

  9. Linux 系统的基本操作及工具的使用

    基本操作命令如:useradd.userdel.passwd.su 添加用户.删除用户.修改密码.切换用户 ls.ll.cd.cp.mv.chmod ps.kil.man mkdir.touch.ta ...

  10. seleniumGrid分布式远程执行测试脚本

    执行UI自动化测试脚本时,不仅可以在本地执行,也可以远程指定某计算机执行测试脚本,seleniumGrid为我们提供了这类服务,但还需要自己搭建环境. 一.本地计算机需要准备java环境和seleni ...