1. 案例介绍
(1) 系统背景
- 一个百万计注册用户的在线教育平台;
- 主要目标用户群体是几岁到十几岁的孩子;
- 注册用户大概几百万规模,日活用户规模大概在几十万;
- 用户白天上学,一般晚上放学之后到八九点,还有周末,都是是这个平台最活跃的时候;
- 晚上两三个小时是这个平台的每天的高峰期,99%的流量都集中在晚上;
这个平台的高频操作就是上课,像浏览课程详情、下单付费、选课排课都是低频行为,低频行为我们暂时忽略掉;
(2) 核心业务流程
孩子们在上课的时候,核心的业务流程就是大量的互动环节,在娱乐游戏中进行教学;
比如在完成什么任务的时候必须点击很多的按钮,频繁的进行互动,然后系统后台需要接收大量的互动请求,并且记录下来用户的互动过程和互动结果,做了多少个任务,对了几个,错了几个;
(3) 高峰运行内存压力分析
每秒请求压力
- 假设晚上高峰3小时共有60万活跃用户,平均每个用户大概使用1个小时上课,那么每小时大概20万活跃用户;
- 假设每个用户,一分钟进行1次互动操作,即一个小时进行60次互动操作;
- 20万用户在1小时内会进行1200万次互动操作,即平均每秒大概3000次的操作;
- 假设部署5台4核8G的机器,每台机器每秒产生600个请求;
- 每秒内存使用压力
- 一次互动请求不会有太复杂的对象,主要记录一些用户互动过程,可能跟一些积分有关联的东西;
- 假设一次互动请求会创建几个对象,占5KB的内存;
- 每秒会在内存中创建 600*5=3MB 对象;
2. 基于内存使用模型进行 JVM 优化
4核8G的机器,每台机器每秒会有600个请求,会占用 3MB 的内存空间;
(1) G1 默认内存布局和设置
假设分配给堆内存 4G,其中新生代初始占比5%,最大占比60%,每个线程栈内存为1M,方法区的内存一般为256MB,JVM 参数如下:
- 每个 Region 大小 4096/2048=2M;
- 新生代初始 Region 数量 2048*0.05=100 个,共有 200M 空间;
每触发一次 GC 的时候导致的 Stop the World 时间不超过 200ms;
-Xms4096M -Xmx4096M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseG1GC
(2) 多长时间会触发新生代 GC?
触发机制与 ParNew 不一样:
- ParNew 新生代 GC 触发时机:Eden 区域的空间不够了,就触发 Minor GC;
- G1 新生代 GC 触发时机:根据你预设的“-XX:MaxGCPauseMills”GC 停顿时间,给新生代分配一些 Region,然后到一定程度就触发 GC,并且把 GC 时间控制在预设范围内,尽量避免一次性回收过多的 Region 导致 GC 停顿时间超过预期;
示例:
首先需要给 JVM 堆内存分配足够的内存;
合理的设置“-XX:MaxGCPauseMills”GC 停顿时间,尽量让系统的 GC 频率别太高,同时每次 GC 停顿时间别太长,达到一个理想的合理值;
首先,mixed gc 的触发时机:老年代在堆内存占比超过 45% 就会触发;
- 优化思路:避免对象过快的进入老年代,避免频繁触发 mixed gc;
- 其次,核心还是“-XX:MaxGCPauseMills”的合理设置;
- 在保证新生代 gc 不太频繁的同时,还得考虑每次 gc 过后的存活对象有多少,避免存活对象太多快速进入老年代,频繁触发 mixed gc;