1

摘要

我们在项目开发中,一般首先需要在项目上线前对项目的JVM内存大小需要进行设置。所以我们一般的思路是:线上业务系统流程梳理->每天的数据访问量->高峰期的QPS->根据QPS计算出计算机机器配置(1、需要多少台机器 2、每台机器的内存多大 3、每台机器的JVM参数设置)->系统扩充10-20倍数预估(一般我们的系统都会比预估的大10-20倍);

其中每台机器的JVM内存参数设置是关键:需要我们根据并发、每一次请求的数据大小、以及每一次请求的处理时长来计算机器每次会进入多大数据量到Java堆内存的新生代触发Minor GC/Young GC。

内容

目前根据所在项目的业务背景做一个大概的业务流程整理,然后根据机器的配置分析设置JVM的内存参数。

线上系统业务流程抽象

首先是核心业务流程简化、抽象以及梳理。目前项目上是做智能外呼系统,核心流程就是第三方系统/应用系统前端导入数据入库,然后会创建相应的任务规则,外呼平台根据相应的任务规则进行呼叫,由于呼叫规则比较复杂,外呼平台发起呼叫的时候,需要调用应用系统核心两个接口:
1、外呼重呼轮呼接口:主要是针对1人多号码情况下,针对于未接通或者接通的号码做是否需要重新呼叫这个号码,如果不在重呼,是否需要发起轮呼。
2、号码防火墙接口:此接口主要是平台发起呼叫时候的号码校验,一个号码是否在白名单、黑名单、以及每天是否呼叫限制,以及一些号码变换(根据业务号码获取真实号码,被叫号码解密)

然后平台呼叫完毕之后,会将呼叫结果入库存储,然后应用系统的定时调度会每次查询出100条数据接口返回给第三方系统。简化的业务流程图如下:
image.png

系统QPS

系统呼叫结果量高峰期大概20万,号码接通率平均50%,未呼通的号码平均呼叫3次,一个可能多个号码,我们按照1人一号码算,呼叫结果传输根据我们三方系统的个数,有不同的定时传送任务。目前对接的三方系统有4个。所以我们预估每天的进入智能外呼系统的数据量有:
总数据量=20万(外呼平台-号码防火墙接口)+10万(接入数据)+15万(重呼轮呼接口)+20万(结果回传)+5万(其他)=70万,每天呼叫时间大概为8个小时;由于外呼系统呼叫是自动后台触发,不存在80%的数据量在20%的时间涌入。所以QPS为:700000/8/60/60=25;

JVM参数预估.

每秒有多少次请求
每秒25个请求

每次请求耗时
20万(外呼平台-号码防火墙接口--平均1秒)+10万(接入数据-8秒)+15万(重呼轮呼接口-平均耗时3秒)+20万(结果回传-200毫秒)+5万(其他)=70万

号码防火墙接口:平均1秒
image.png

接入数据耗时:我们大概算8秒.
image.png

重呼:我们大概算3秒.
image.png.

结果回传:平均200毫秒
image.png

合计每次请求耗时大概:(20+10x8+15x3+20x0.2+5)/70=2.17秒。

每个请求大概需要多大的内存空间?
每个请求数据平均来说有20多个字段,大概500b

每秒发起的请求对内存的占用?
每秒有25个请求,每个请求大小是500b,所以每秒发起的请求对内存的占用为:25x500b=12.2kb

对完整的系统内存占用需要进行预估.
我们目前只是对系统外呼时候的接入数据量进行评估,真实外呼系统运行,肯定会每秒创建大量其他对象,更有可能在某一个时刻数据过来的多时候触发更大的数据并发,结合此我们可以大概估算整个系统每秒钟大概有多少内存空间,我们再次基础之上将计算结果扩大10~20倍。也就是说,每秒钟在java堆内存中创建的为:120kb-250kb之间,下一秒继续触发,一次新生代垃圾将会越来越多,然后会触发Minor GC回收掉垃圾。

系统的JVM堆内存应该怎么设置?

其实一般上线业务系统,常见的机器配置是2核4G,或者是4核8G;
按照2核4G来计算:
4G内存分配JVM虚拟机内存为2G;JVM虚拟机运行时候除了Java堆内存、Java虚拟机栈、程序计数器、方法区;我们分配给Java对内存的大小一般就是占一半多,也就是1G多,Java堆有新生代跟老年代,按照java对象大都是"朝存夕亡",我们的新生代跟老年代大约是8:2也就是新生代是800M;每秒系统有250kb的新生代对象进入;那么800x1024/250/60=55min;也就是55分钟左右会触发一个Minor GC,所以已经足够。

并发扩大10倍系统的JVM堆内存应该怎么设置?

如果系统并发增加10倍,也就是每秒250个请求的话,那么每秒将会有:1200kb-2500kb数据进入java堆内存,此时就算是2M;800M的java堆内存将会在:800/2/60=7分钟,大概7分钟左右就会触发一次Minor GC,频繁触发会影响系统系能,

如果采用4核心8G的话,我们4G的内存分配给JVM虚拟机,然后Java堆内存是2G,新生代占用:1.6G,那么1.6x1024/2/60=13分钟,此时也会造成Minor GC频繁,所以我们可以让JVM虚拟机为5G,然后java堆内存为3G,新生代为:2.4G,那么:2.4x1024/2/60=21分钟,大概21分钟触发一次Minor GC还算凑合。

所以参数设置:

-Xms3G -Xmx3G -Xmn2.4G

不合理设置JVM参数问题

不合理的设置JVM参数的话,在并发量比较大的情况下,如果机器本身的内存比较少为:2核4G,分配的java堆内存比较小,比如java堆内存里面的新生代跟老年代各分配500M,业务并发量是1000,每秒数据量对象是几十M,并且线程、网络、内存等多种因素导致每个请求的时间变长,进而最坏的结果是:每秒又几十M的对象进入新生代,然后新生代内存满了之后会频繁触发minor gc,由于处理延时,在经过十几次minor gc之后,请求数据还没有处理完,java堆内存里面的对象还存活,所以会进入老年代,老年代也会越来越多,然后老年代垃圾也会越来越多,进而导致老年代频繁的垃圾回收,由于老年代的垃圾回收采用"标记-整理"法,效果很差,最后会导致系统系能很差。

如何设置永久代大小?

一般永久代刚开始上线一个系统,没太多参考规范,一般设置个大概几百MB,大体上是够用的,因为里面主要是存放一些类的信息.

如何设合理设置栈内存大小?

栈内存大小,一般不会特别去预估和设置的,一般默认是比如512KB到1MB,就差不多了,这个是每个线程自己的栈内存空间,存放线程执行方法期间的各种布局变量的。


startshineye
91 声望26 粉丝

我在规定的时间内,做到了我计划的事情;我自己也变得自信了,对于外界的人跟困难也更加从容了,我已经很强大了。可是如果我在规定时间内,我只有3分钟热度,哎,我不行,我就放弃了,那么这个就是我自己的问题,因为你自己...