您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~
前面说过,JVM会将堆内存划分为年轻代、老年代两个区域。年轻代会将创建和使用完之后马上就要回收的对象放在里面,而老年代则将创建之后需要长期存在的对象放在里面。那么现在再来看一个比较具体的例子。
在电商系统中,支付系统大概处于这样的位置:
核心业务流程是:
假设日活在100W,那么JVM会创建和销毁100万个支付订单。那么问题来了:
1、需要部署多少台机器?
2、每台机器需要多大内存?
3、每台机器上启动的JVM需要分配多大堆内存空间?
4、JVM需要分配多大内存空间才能保证不会崩溃?
假设现在面试官坐在你对面,这灵魂四连问,你该怎么回答?
所以,需要合理地设置JVM堆参数,依据是:
首先要明确的,就是每秒钟要处理多少笔订单,这里是100万笔,那么:
1、每天100万单,都分别在两个高峰期:中午和下班后,每个高峰持续2小时,就是:2 × 2 = 4小时;
2、4小时 = 4 × 3600 = 14400秒;
3、100万 ÷ 14400秒 = 69.4单/秒,将系统性能弄紧凑一些,可以按150单/秒算;
4、假设支付系统部署了3台机器,且采取流量均分策略,那么每台机器至少每秒需要处理50个订单:支付订单请求 -> JVM创建支付订单对象 -> 写入数据库 -> 处理其他事务 -> 返回数据(不含网络请求时间损耗),假设理想状态下需要花费20毫秒时间,那么:
l 接收到50笔支付订单请求 -> 在JVM年轻代中创建50个订单支付对象 -> 50 × 20毫秒=1秒之后处理完毕
l JVM将引用收回,这些对象就成了年轻代中的垃圾对象
l 下一秒继续重复上述过程
5、每笔支付订单所需的内存空间,依据实例对象及变量类型计算:
l 每个实例对象的Java基本类型所占据的空间 + 引用对象所占据的空间 ≈ 1KB;
l 50笔支付订单 = 50K内存空间;
6、JVM中的对象创建持续累积:
按照每秒消耗50KB的速度,如果没有垃圾回收,那么一台内存8G的机器大概会经过46个小时之后,将内存耗尽(8 × 1024 × 1024K = 8388608 / 50 = 167772 秒 / 3600 = 46小时);
服务器肯定不会将全部内存给应用运行,最多20%~40%,资源实际耗尽时间更短;
此时,垃圾回收出现,清场,腾位置,应用继续运行,这个过程循环往复;
7、真实的系统资源消耗,会比理想条件下的预估高出20倍以上。
所以,这里给出建议的配置方案:
1、无脑化通用配置:2C4G/4C8G;
2、建议有条件,只考虑4C8G及以上(硬件成本还会继续下降);
3、单台:-Xms3072M -Xmx3072M -Xmn2048M;
4、如果业务量更大,可以不只部署3台,可以是5台,10台或更多。
大促期间,访问量暴增10~100倍,因为压力骤增,部分请求出现超时甚至卡死、挂掉。这部分特别慢且未被释放的请求,可能会被GC误移入老年代,导致老年代里也出现越来越多的垃圾对象,由此:
年轻代资源不足 -> 年轻代频繁GC -> 老年代资源不足 -> 老年代频繁GC
依据经验,一般情况下:
JVM栈上线初期:512K~1M足够
永久代上线初期:512M~1G足够
FAQ:
1、方法执行完后,栈帧立马出栈,因此该栈帧中的变量等数据立即就被回收了;
2、项目中托管给Spring管理的对象,带@Configration的都是长期存在于老年代;
3、自定义的bean对象如果不被定义为类对象就是朝生夕灭的,分配在年轻代中;
4、内存不够才会回收软引用对象,内存空间足够的话,不会回收软引用对象;
5、弱引用不管内存空间够不够,只能撑到下次垃圾回收之前,就被会回收;
6、垃圾回收的是软引用,弱应用和虚引用;
7、并不是新生代全部占满才minor gc,而是只要里面一块主要的内存区域满了就minor gc;
8、通过合理的估算方式尽量设大新生代 ,让系统在高峰期不进行垃圾回收。
感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~