# (一)概述 {#一-概述}
如何给JVM虚拟机巧妙地设计参数对大部分开发来说一直是个随缘的事情,可能是去网上拷贝一套参数,可能是沿用公司其他应用的参数。但是这个随缘的操作可能就会给未来留下隐患。给JVM分配的内存过大倒是没什么问题,无非浪费点资源,但是如果分配的内存过小,就有可能导致频繁的Full GC,给人一种系统一直很卡的感觉。这篇文章就通过一个实例分析一下如何结合场景设置JVM虚拟机参数。
当然,本文更重要的是希望能通过预估参数的这个过程,让你更加了解虚拟机内部的一些东西,要想最准确的参数设置,用一些工具记录下JVM各个区域的变化会更有效。
# (二)前置准备 {#二-前置准备}
系统基于JDK1.8,堆结构如下。
为了方便理解业务,本文以电商的交易系统为例进行讲解。在微服务架构下,目前主流的互联网公司都会把自己的业务拆分成多个服务架构,比如电商系统会分为交易微服务、购物车微服务、商品微服务等等,可能这个粒度会更细。一个底层架构会将这些微服务集成起来。实际上就是一个大的容器里放了一个个jar包。
# (三)通常业务场景下的预估流程 {#三-通常业务场景下的预估流程}
一个交易微服务中会涉及到订单对象、优惠券对象、用户对象、交易记录对象等一系列对象,我们可以简单预估在一次交易中这些对象会占用的空间。预估的方式也很简单,八种基本类型直接带入字节大小,对象类型以基本类型为基础预估大小。只需要一个大致的值就行。
比如每次交易中一个订单对象大约是1kb,优惠券对象2kb,用户对象4kb,交易记录对象4kb,除此之外还可能会存在的List集合、数组等等。大约一次交易中产生的对象大约在25kb左右。
一个每日交易量在100万的系统,交易量主要集中在6个小时中,平均每秒最大会有40笔订单的产生。也就意味着每秒产生对象大小是1M。这些产生的对象在一次交易结束后都会被当成垃圾,也就意味着每秒会产生1M的垃圾。
假设我们只有一台2核4G的服务器,分配给堆的内存一般就1.5G左右,通过计算,可以算出堆中每个区域的大小,如下图:
通过计算可以得出,每400秒,400M的Eden区就满了,会进行一次young GC。98%的垃圾会被回收,意味着将会有8M左右的垃圾进入在survivor转移。一些对象在经过几次young GC之后会进入到老年代中,这种情况Full GC的频率会很低。虽然400秒一次youngGC略微还是快了些,但是对于系统而言基本上没有影响。
# (四)特殊业务场景下的预估流程 {#四-特殊业务场景下的预估流程}
现在公司打算开展一次一小时的补贴活动,在活动的这一个小时时间内,订单数量可能会是之前的20倍,也就意味着每秒会有800笔订单的产生,每秒会产生20M的垃圾,这下会发生什么呢?
Eden区20秒就被占满,20秒执行一次youngGC ,此时由于订单过于多,可能部分接口响应会达到几秒甚至几十秒,这些对象在经过几次youngGC之后就会逐步就会进入到老年代中。一般在线上一个小时内出现2次以上FullGC就得告警了。
这种情况下就意味着我们对机器资源以及JVM虚拟机内存需要重新考虑了。
首先考虑提升JVM虚拟机内存,由于硬件限制,JVM虚拟机内存的提高首先要提高机器的性能,我们从双核4G升级成4核8G。分配给堆4.5G的内存。这个时候Eden区就会有1200M的内存,同样条件下,1分钟才会执行一次youngGC。20秒提高到1分钟能保证响应慢的接口对象也能在youngGC中被消灭,而不会进入到老年代中。
同时我们可以把一台机器升级为3台机器,负载均衡后每台机器的订单压力是原来的1/3,youngGC时间提升为原来的3倍,同时接口响应时间加快。基本上3台4核8G的机器就能满足这次活动。
# (五)总结 {#五-总结}
预估之后,并非意味着就完全没问题了,还需要在上线时备好更多机器,防止意外发生。实践能给你带来最好的答案。