每天100w次登录请求,8G内存该如何设置JVM参数?
每天100w次登录请求,8G内存该如何设置JVM参数?
大概可以分为以下步骤:
Step1: 新系统上线如何规划容量?
1、套路总结
任何新的业务系统在上线以前都需要去估算服务器配置和JVM的内存参数,这个容量与资源并不仅仅是系统架构师的随意估算的,需要根据系统所在业务场景去估算,推断出一个来系统运行的模型,评估JVM 性能和GC 频率等等指标
① 计算业务系统每秒钟创建的对象会占用多大的内存空间,然后计算集群下的每个系统每秒的内存占用空间(对象创建速度)
② 设置一个机器配置,估算新生代的空间,比较不同新生代大小之下,多久触发一次MinorGC
③ 为了避免频繁GC,就可以重新估算需要多少机器配置,部署多少台机器,给JVM多大内存空间,新生代多大空间。
④根据这套配置,基本可以推算出整个系统的运行模型,每秒创建多少对象,1s以后称为垃圾,系统运行多久新生代会触发一次GC, 频率多高。
2、套路实战--以登录系统为例
① 假设每天100w次登录请求,登录峰值在早上,预估峰值每秒100次登录请求。
② 假设部署3台服务器,每台机器每秒处理30次登录请求,假设一个登录请求需要处理1秒正,JVM 新生代里每秒就要生成30个登录对象,1s之后请求完毕这些对象就成为了垃圾。
③一个登录请求对象假设20个字段,一个对象估算500字节,30个登录占用15kb,考虑到RPC和DB 操作,网络通信、写库、写缓存一顿操作下来,可以扩大到20-50倍,大约1s 产生几百k-1M数据,
④ 假设2C 4G 机器部署,分配2G 堆内存,新生代则只有几百M按照1s1M 的垃圾产生速度,几百秒就会触发一次MinorGC 了。
⑤ 假设4C8G 机器部署,分配4G堆内存,新生代分配2G,如此需要几个小时才会触发一次MinorGC
所以,可以粗略的判断出来一个每天100w次请求的登录系统,按照4C 8G的3实例集群配置,分配4G 堆内存、2G 新生代的JVM,可以保证系统的一个正常负载。
step2: 该如何进行垃圾回收器的选择?
吞吐量还是响应时间
首先,引入两个概念: 吞吐量和低延迟。
吞吐量 = CPU 在用户应用程序运行的时间/ (CPU在用户应用程序运行的时间 +CPU 垃圾回收的时间)
响应时间 = 平均每次的GC 耗时
堆内存增大, gc 一次能处理的数量变大,吞吐量变大;但是gc 一次的时间会变长,导致后面排队的线程等待时间变长;相反,如果堆内存变小,gc 一次时间短,排队等待的线程等待时间变短,延迟减少,但一次请求的数量变少(并不绝对符合)
垃圾回收器设计上的考量
① jvm在GC时不允许一遍垃圾回收,一遍创建新的对象(就像不能一遍打扫卫生,还在一边仍垃圾)
② JVM 需要一段stop the world 的暂停时间,而STW 会造成系统短暂停顿不能处理任何请求;
③ 新生代收集频率高,性能优先,常用复制算法;老年代频次低,空间敏感,避免复制方式。
④ 所有垃圾回收器的涉及目标都是要让GC 频率更少,时间更短,减少GC 对系统影响!
目前主流的垃圾回收器配置的是新生代采用的ParNew, 老年代采用的是CMS 组合方法是,或者是完全采用G1回收器,
从未来的趋势来看,G1 是官方维护和更为推崇的垃圾回收器。
业务系统:
延迟敏感的推荐CMS:
大内存服务,要求高吞吐的,采用G1回收器!
Step3: 如何对各个分区的比例、大小进行规划
一般的思路为:
首先,JVM 最重要最核心的参数是去评估内存和分配,第一步需要执行堆内存的 大小,这个是系统上线必须要做的,-Xms 初始堆大小,-Xmx 最大堆大小,后台Java 服务中一般都指定为系统内存的一半,过大会占用服务器的系统资源,过小则无法发挥JVM 的最佳性能。
其次,需要指定-Xmn 新生代的大小,这个参数非常关键,灵活度很大,虽然sun 官方推荐为 3/8 大小,但是要根据业务场景来定,针对于无状态或者轻状态服务来说,一般新生代甚至可以给到堆内存的3/4 大小。而对于有状态服务,新生代可以按照默认比例1/3 来设置。服务有状态,则意味着会有更多的本地缓存和会话状态信息常驻内存,应因为要给老年代设置更大的空间来存放这些对象。
JVM参数 | 描述 | 默认 | 推荐 |
-Xms | Java 堆内存的大小 | OS内存64/1 | OS内存一半 |
-Xmx | Java堆内存的最大大小 | Os内存4/1 | OS内存一半 |
-Xmn | Java堆内存中的新生代太小,扣除新生代剩下的就是老年代的内存大小了。 | 默认为堆的1/3 | sun推荐3/8 |
-Xss | 每个线程的栈内存大小 | 和jdk有关 | sun |
拓展: JVM 的垃圾回收制度为---- 分区分为 新生代、老年代。新生代又分为Eden 区、SurvivorFrom 、SurvivorTo 三个区
Eden区: java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden 区内存不够的时候就会触发MinorGC , 对新生代进行一次垃圾回收。
survivorFrom : 保留了一次MinorGC 过程中的幸存者。
SurvivorTo: 上一次GC 的幸存者,作为这一次GC的被扫描者。
也就是说,将Eden 和survivoFrom 复制到 to 区,然后清空 Eden 和survivoForm区的对象。然后 to 与 From 互换.(类似与汉诺塔)