linux设置jvm内存参数_每天百万交易的支付系统,生产环境该怎么设置JVM堆内存大小...

各位志同道合的朋友们大家好,我是一个一直在一线互联网踩坑十余年的编码爱好者,现在将我们的各种经验以及架构实战分享出来,如果大家喜欢,就关注我,一起将技术学深学透,我会每一篇分享结束都会预告下一专题

前几天我们分享了我们java中的基石JVM的相关内存模型(想要精通java,必须要懂Java内存模型,一点不忽悠)以及类加载机制(java类加载机制,轻松掌握,再也不怕面试官刁难),其实在我们平时开发中,大部分工程师在生产环境部署java的时候很少去思考这个JVM的相关参数到底怎么去设置,更很少去预估我们程序所需的内存以及并发量等等,或者是直接参考公司资深工程师写的配置。

这样的工程师虽然不能说你不够专业,但是我觉得你应该具备这样的思考能力,只有这样你才能才遇到自己程序各种线上异常的时候不用去请教公司大拿,你就应该自己成为这方面的大拿。好,那我们今天就结合实际案例来每日百万交易的支付系统,生产环境该怎么去设置JVM堆内存大小。

背景

支付在我们如今的生活中已无处不在,它时刻围绕着我们的吃喝住行,比如我们在电商里面购买一个商品的时候,就会涉及到支付系统,大体的流程是这个样子的:

5dcfd16c9d2fa70f36f09851333fc9b5.png

从上图中,我们能看出支付系统是我们整个交易过程中核心且是不可或缺的系统之一,只有商家收到钱,才能卖给你东西。下面我们就来看看如何根据这个支付系统未来预估的业务量,访问量,去推算这个系统每秒种的并发量,然后推算每秒钟的请求对内存空间的占用,进而推算出整个系统运行期间的JVM内存运转模型,只有这样我们才能很好的去合理的去设置JVM堆内存。

每日百万的支付系统压力体现在何处?

在这里我建议,在做任何系统的时候,我们都要去思考我们现在开发的这个系统将要面临什么压力,而这个压力又会体现在我们系统的哪个地方。那对于我们现在的支付系统对于我们的JVM来说,他的核心压力就在于,不停的创建支付单,支付单包括上游系统信息、创建时间、商品信息、用户信息、支付渠道等等。也就是说,我们的JVM中每天会有上百万的支付订单对象在频繁的创建和销毁。

900d1007d045ba2e5e64c2f4b51ad13f.png


弄清楚了我们系统的压力之后,我们接下来就要去思考另外一些直接影响我们业务的几个核心问题:

  • 我们的支付系统需要部署多少台机器。
  • 每台机器需要多大的内存。
  • 每台机器上启动的jvm需要给多大的内存,才能支撑我们百万的订单创建而不会内存溢出。

支付系统每秒钟需要处理多少笔支付订单

要想合理设置我们JVM堆内存大小,我们就得去计算出我们系统每秒会处理多少笔支付单。

我们都知道,在任何一个系统都会有高峰期和平稳期的,我们就可以拿高峰期来计算就行了。现在我们系统每天有100万的支付订单,高峰期一般在中午或者晚上,高峰期大概3个小时左右,也就是每秒大概有100个订单产生。

这样的话,我们就可以假设将支付系统先部署个3台机器,每台机器最普通的配置4CPU 8G的。也就是每台机器每秒处理大概30多个订单。

89f60dec37b654ba3225057a57846f51.png

每个支付订单耗时多久

接下来,我们需要搞清楚另一个重要的问题,即每个支付订单大概要多长时间才处理的完?

从用户发送的一次支付请求,到JVM里创建支付单对象相关操作,再到持久化到数据库,或者调用第三方支付这一流程。现在我们就假设这些流程完成需要1秒的时间。

现在我们支付系统的模型是这个样子了:

  1. 每台机器每秒钟会收到30个支付订单的请求,接着就会在JVM的新生代内存区域创建30个支付订单对象,最后做相关写入数据库的相关操作;
  2. 然后1秒之后,这30个订单就处理完了,这些订单在JVM中就会成为没被引用的垃圾对象;
  3. 接着下一秒又来了30个支付单请求,重复之前的动作。

每个支付订单大概需要多大的内存空间

下面我们就要来算一算我们一个支付订单大概需要多少内存空间,这里教大家怎么去算一下,就是主要根据你的实体类的变量类型去算就行了,即Integer类型的变量占4个字节,Long类型的占8个字节,其他的网上查查哈,这样就可以推算出我们的当前实体类所需的内存大小了。

public class payOrder { private Integer orderId; private Integer userId; private Long createTime;}

像我们这样的支付订单实体类大概也就20几个字段在几百个字节的样子,我们就算大一点就假设它500字节好了,不到1Kb

每秒发起的支付请求会占用多少内存

上面我们推算出来了每秒钟多少请求,同时推算了单个支付单的内存大小,现在我们看看每秒发起的请求一共要占用多少内存。

3e37af2e8609904c9e68fda0612b6f33.png

从图中也可以看出,每台机器每秒共30个支付单,也就是大概占用30 * 500字节 = 15000字节,大概也就在15Kb的样子,很小吧。

让支付系统跑起来

通过上面的分析,我们已经很清晰的知道了我们当前系统的各个核心问题,也弄清楚了每秒多少请求,每秒占用多少内存空间。现在我们先将我们的支付系统在我们脑海里运行一遍:

  1. 每秒30个请求,创建30个支付单对象,大概也就在kb级别的内存占用。
  2. 1秒之后,这30个对象就不会被引用,会成为JVM的新生代的垃圾
  3. 下一秒的时候,又有30个请求过来,新生代又会继续产生这些垃圾对象
  4. 如此往复着,知道有一刻,新生代里有几十万个对象了,大概占用几百兆的内存了,都快满了。
  5. 这个时候就会触发Minor GC,回收掉新生代的那些垃圾对象,以腾出更多空闲空间来让新的对象进来

好,这就是目前我们支付系统的运行模型,我们要在脑海里先自己跑一遍,才能更好的去配置生产环境。

对整个支付系统进行内存预估

我们上面估算的那些内存其实只是估算最核心的部分,真实的系统中,可能会随带好多对象的创建,所以依据我们的经验来看,将核心部分扩大10到20被就差不多了。也就是,我们现在的支付系统大概在几百KB~1M之间。

然后每秒大概就有1M左右的垃圾对象存在新生代中,一直循环多次后,就会触发Minor GC回收掉这些垃圾,到目前为止我们就把完整的支付系统给运行起来了,最后,我们就要对其进行JVM内存设置了。

JVM堆内存该如何设置

这里建议,生产环境服务器至少最普通是4CPU 8G 来部署我们的支付系统,那么我们的JVM进程至少可以给4G以上内存,新生代在里面至少可以分配到2G内存空间,这样子就可以做到可能新生代每秒钟1MB左右的内存,但是需要将近半小时到1小时才会让新生代触发Minor GC。基本还可以的接受的,只有GC相关专题,我会在后面写的,敬请期待哈。

所以,在我这个支付系统里面,-Xms和-Xmx设置为3G,给整个堆内存3G内存空间,-Xmn设置为2G,给新生代2G内存空间。

当然,如果我们业务还需增长,我就部署5台机器或者10台机器,每台机器还可以申请更高的内存空间16G乃至32G都可以。

总结,今天我带着大家来分析了一个系统开发完,我们该怎么去再生产环境配置我们的JVM堆内存大小,主要需要考虑我们应该部署多少台机器?每台机器要多少内存?每台机器每秒钟能有多少的请求过来并且生成对象?然后基于这些参数判断多久触发一次Minor GC,这样一套流程下来,我们就能很好的掌握并且自己可根据复杂的业务变化来设置自己系统的JVM堆内存,如果大家喜欢就关注我,我会一直分享业界流行技术方案,让我们共同学习共同进步。

java高并发、高级面试、分布式、微服务、人工智能以及Python等各大互联网专题,关注wx公众号【架构师修炼】专属视频资料点击菜单“成神之路” 你会感谢我的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM参数设置详细说明、JVM 参数设置详细说明 1: heap size a: -Xmx 指定jvm的最大heap大小,如:-Xmx=2g b: -Xms 指定jvm的最小heap大小,如:-Xms=2g,高并发应用,建议和-Xmx一样,防止因为内存收缩/突然增大带来的性能影响。 c: -Xmn 指定jvm中New Generation的大小,如:-Xmn256m。这个参数很影响性能,如果你的程序需要比较多的临时内存,建议设置到512M,如果用的少,尽量降低这个数值,一般来说128/256足以使用了。 d: -XX:PermSize= 指定jvm中Perm Generation的最小值,如:-XX:PermSize=32m。这个参数需要看你的实际情况,可以通过jmap命令看看到底需要多少。 e: -XX:MaxPermSize= 指定Perm Generation的最大值,如:-XX:MaxPermSize=64m f: -Xss 指定线程桟大小,如:-Xss128k,一般来说,webx框架下的应用需要256K。如果你的程序有大规模的递归行为,请考虑设置到512K/1M。这个需要全面的测试才能知道。不过,256K已经很大了。这个参数对性能的影响比较大的。 g: -XX:NewRatio= 指定jvm中Old Generation heap size与New Generation的比例,在使用CMS GC的情况下此参数失效,如:-XX:NewRatio=2 h: -XX:SurvivorRatio= 指定New Generation中Eden Space与一个Survivor Space的heap size比例,-XX:SurvivorRatio=8,那么在总共New Generation为10m的情况下,Eden Space为8m i: -XX:MinHeapFreeRatio= 指定jvm heap在使用率小于n的情况下,heap进行收缩,Xmx==Xms的情况下无效,如:-XX:MinHeapFreeRatio=30 j: -XX:MaxHeapFreeRatio= 指定jvm heap在使用率大于n的情况下,heap 进行扩张,Xmx==Xms的情况下无效,如:-XX:MaxHeapFreeRatio=70 k: -XX:LargePageSizeInBytes= 指定Java heap的分页页面大小, 如:-XX:LargePageSizeInBytes=128m 2: garbage collector a: -XX:+UseParallelGC 指定在New Generation使用parallel collector,并行收集,暂停,app threads,同时启动多个垃圾回收thread,不能和CMS gc一起使用。系统吨吐量优先,但是会有较长长时间的app pause,后台系统任务可以使用此 gc b: -XX:ParallelGCThreads= 指定parallel collection时启动的thread个数,默认是物理processor的个数 c: -XX:+UseParallelOldGC 指定在Old Generation使用parallel collector d: -XX:+UseParNewGC 指定在New Generation使用parallel collector,是UseParallelGC的gc的升级版本,有更好的性能或者优点,可以和CMS gc一起使用 e: -XX:+CMSParallelRemarkEnabled 在使用UseParNewGC的情况下,尽量减少mark的时间 f: -XX:+UseConcMarkSweepGC 指定在Old Generation使用concurrent cmark sweep gc、gc thread和app thread并行(在init-mark和remark时pause app thread)。app pause时间较短,适合交互性强的系统,如web server g: -XX:+UseCMSCompactAtFullCollection 在使用concurrent gc的情况下,防止memory fragmention,对live object进行整理,使memory 碎片减少 h: -XX:CMSInitiatingOccupancyFraction= 指示在old generation 在使用了n%的比例后,启动concurrent collector,默认值是68,如:-XX:CMSInitiatingOccupancyFraction=70 有个bug,在低版本(1.5.09 and early)的jvm上出现, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486089 i: -XX:+UseCMSInitiatingOccupancyOnly 指示只有在old generation在使用了初始化的比例后concurrent collector启动收集 3:others a: -XX:MaxTenuringThreshold= 指定一个object在经历了n次young gc后转移到old generation区,在linux64的java6下默认值是15,此参数对于throughput collector无效,如:-XX:MaxTenuringThreshold=31 b: -XX:+DisableExplicitGC 禁止java程序中的full gc,如System.gc()的调用。最好加上么,防止程序在代码里误用了。对性能造成冲击。 c: -XX:+UseFastAccessorMethods get、set方法转成本地代码 d: -XX:+PrintGCDetails 打应垃圾收集的情况如: [GC 15610.466: [ParNew: 229689K->20221K(235968K), 0.0194460 secs] 1159829K->953935K(2070976K), 0.0196420 secs] e: -XX:+PrintGCTimeStamps 打应垃圾收集的时间情况,如: [Times: user=0.09 sys=0.00, real=0.02 secs] f: -XX:+PrintGCApplicationStoppedTime 打应垃圾收集时,系统的停顿时间,如: Total time for which application threads were stopped: 0.0225920 seconds 4: a web server product sample and process JAVA_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 最初的时候我们用UseParallelGC和UseParallelOldGC,heap开了3G,NewRatio设成1。这样的配置下young gc发生频率约12、3秒一次,平均每次花费80ms左右,full gc发生的频率极低,每次消耗1s左右。从所有gc消耗系统时间看,系统使用率还是满高的,但是不论是young gc还是old gc,application thread pause的时间比较长,不合适 web 应用。我们也调小New Generation的,但是这样会使full gc时间加长。 后来我们就用CMS gc(-XX:+UseConcMarkSweepGC),当时的总heap还是3g,新生代1.5g后,观察不是很理想,改为jvm heap为2g新生代设置-Xmn1g,在这样的情况下young gc发生的频率变成7、8秒一次,平均每次时间40-50毫秒左右,CMS gc很少发生,每次时间在init-mark和remark(two steps stop all app thread)总共平均花费80-90ms左右。 在这里我们曾经New Generation调大到1400m,总共2g的jvm heap,平均每次ygc花费时间60-70ms左右,CMS gc的init-mark和remark之和平均在50ms左右,这里我们意识到错误的方向,或者说CMS的作用,所以进行了修改。 最后我们调小New Generation为256m,young gc 2、3秒发生一次,平均停顿时间在25毫秒左右,CMS gc的init-mark和remark之和平均在50ms左右,这样使系统比较平滑,经压力测试,这个配置下系统性能是比较高的。 在使用CMS gc的时候他有两种触发gc的方式:gc估算触发和heap占用触发。我们的1.5.0.09 环境下有次old 区heap占用在30%左右,她就频繁gc,个人感觉系统估算触发这种方式不靠谱,还是用 heap 使用比率触发比较稳妥。 这些数据都来自64位测试机,过程中的数据都是我在jboss log找的,当时没有记下来,可能存在一点点偏差,但不会很大,基本过程就是这样。 5: 总结 web server作为交互性要求较高的应用,我们应该使用Parallel+CMS,UseParNewGC这个在jdk6 -server上是默认的new generation gc,新生代不能太大,这样每次pause会短一些。CMS mark-sweep generation可以大一些,可以根据pause time实际情况控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值