如何预估JVM内存
假设文件上传接口的报文大小限制为5M每个请求的处理时长约5s堆空间为Xmx8g。求该接口能支撑的并发是多少查探JVM参数首先需要明确服务配置的JVM启动参数。可使用jinfo -flags pid命令查看生效的JVM参数通过jmap -heap pid命令查看堆内存的使用情况java -XX:PrintFlagsFinal命令查看JVM的所有参数和默认值。需要重点关注影响内存或GC的参数如老年代和年轻代空间占比-XX:NewRatio2或年轻代大小配置XmnEden和Survivor空间占比-XX:SurvivorRatio8大对象阈值-XX:PretenureSizeThreshold仅parNew或seriral GC有效动态年龄判断机制-XX:TargetSurvivorRatio50老年代空间分配担保机制jdk8默认设置为true老年代可用空间阈值‐XX:CMSInitiatingOccupancyFraction75仅CMS垃圾收集器生效所以除了JVM参数外还需要了解具体使用的垃圾收集器这可能会对部分参数和GC有所影响java -XX:PrintCommandLineFlags -version查看当前版本JDK使用的垃圾收集器若服务的JVM启动参数未指定。jdk8默认使用的是ParallelGC也就是Parallel Scanvenge Serial Old的组合若配置UseParallelOldGC参数则使用Parallel Scanvenge Parallel Old的组合。建立内存模型通过以上命令查看该服务的JVM参数配置为-Xms1g初始堆内存为1g -Xmx8g最大堆内存为8g -Xss256k线程栈空间为256k -XX:MaxPermSize200m最大永久代大小为200M -XX:PermSize200m初始永久代为200M -XX:-UseBiasedLocking 禁用偏向锁默认开启在存在大量锁对象的创建并高度并发的环境下禁用偏向锁能够带来一定的性能优化 -XX:PerfDisableSharedMem用于禁用共享内存性能计数器。在某些情况下可以减少由于磁盘 I/O 导致的垃圾收集GC暂停延迟。 但jps、jstat 和 JConsole 等工具将无法找到 JVM因为它们依赖于这些性能计数器文件。 -XX:PrintGCApplicationStoppedTime在GC日志中打印GC应用暂停时间通过以上配置可以大概清楚该应用的JVM堆内存划分年轻代使用Parallel Scanvenge垃圾收集器老年代使用Serial Old垃圾收集器整堆8G老年代约为8G*2/35.4G缺省NewRatio2年轻代约为2.6Geden区约为2.6G*80%约为2G缺省SurvivorRatio8eden实际可使用空间约为1.4G通过jmap命令查看survivor区各2.6G*10%约260M。根据实际业务情况预估内存和GC频率接下来就可以依据以上内存模型对上面的问题进行推算我们粗略设定该接口产生的对象平均大小为5M。eden可容纳的对象数量为1400M/5M280个对象每个请求平均处理时长约5s在这5s的时间窗口内每秒可接受的并发请求为280/556quests/s。第6s的请求进来JVM需要为这次请求分配5M空间但此时eden空间不足触发minor gc可将第1s请求结束后的垃圾对象进行回收剩下4秒的存活对象大约4*56*5M1120M需要移动到s1区由于s1区的内存仅260M这部分对象会进入老年代等下一次fullGC才会被回收这部分即将消亡的对象进入到老年代显然是不合适的不仅无法及时清理还会导致频繁FullGC。为了避免这些朝生夕死的对象频繁进入老年代就需要控制对象的产生速率和数量。由于缺省JVM参数TargetSurvivorRatio50即根据动态年龄判断机制如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半年龄大于或等于该年龄的对象就可以直接进入老年代。所以我们尽量不要让一次MinorGC存活的对象大小总和超过130M那么实际预估可接受的并发数就应该为130M/5M/5s5requests/s。由于系统中还有大量其他业务的对象产生还应预留出一部分内存。所以可支持的并发数应5。以上都是大体的推测值而实际还是需要对接口进行压测不断调整参数对指标进行监控得出最终系统能够承载的压测结果。下篇文件系统梳理和总结下压测工具的使用和压测过程对指标的监控。