本文共 1980 字,大约阅读时间需要 6 分钟。
准备一个小案例,提供一个接口,每次调用都会生成1个1M的对象,然后方法返回,对象由垃圾收集器自动回收。
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class GCController { @RequestMapping("/gcTest") public String gcTest() { byte[] bytes = new byte[1024 * 1024]; return "success"; }}
测试意图:使用压测工具模拟并发请求这个接口,并通过调整堆内存分配,看看对吞吐量、请求等待时间等指标的影响。
压测工具:apache bench
服务器环境:linux虚拟机,2g内存,单核处理器。
ab -c 10 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:38.675 seconds
吞吐量:2585.64 [#/sec] 用户平均请求等待事件:3.868 [ms] 服务器平均请求处理时间:0.387 [ms]YGC:12566次,耗时:12.336秒,FGC:2次,耗时:0.052,总计耗时:12.387秒
ab -c 100 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:44.511 seconds
吞吐量:2246.62 [#/sec] 用户平均请求等待事件:44.511 [ms] 服务器平均请求处理时间:0.445 [ms]YGC:7851次,耗时:16.673秒,FGC:3次,耗时:0.116,总计耗时:16.789秒
-Xms1500m -Xmx1500m
ab -c 10 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:37.939 seconds
吞吐量:2635.84 [#/sec] 用户平均请求等待事件:3.794 [ms] 服务器平均请求处理时间:0.379 [ms]YGC:261次,耗时:0.462秒,FGC:2次,耗时:0.154,总计耗时:0.616秒
ab -c 100 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:36.389 seconds
吞吐量:2748.05 [#/sec] 用户平均请求等待事件:36.389 [ms] 服务器平均请求处理时间:0.364 [ms]YGC:260次,耗时:0.552秒,FGC:2次,耗时:0.155,总计耗时:0.707秒
-Xms1500m -Xmx1500m -Xmn1000m
ab -c 10 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:36.166 seconds
吞吐量:2765.06 [#/sec] 用户平均请求等待事件:3.617 [ms] 服务器平均请求处理时间:0.362 [ms]YGC:130次,耗时:0.254秒,FGC:2次,耗时:0.213,总计耗时:0.467秒
ab -c 100 -n 100000 http://127.0.0.1:8080/gcTest
测试结果:
总耗时:34.479 seconds
吞吐量:2900.33 [#/sec] 用户平均请求等待事件:34.479 [ms] 服务器平均请求处理时间:0.345 [ms]YGC:130次,耗时:0.479秒,FGC:2次,耗时:0.144,总计耗时:0.623秒。
本次测试模拟接口在高并发频、高频次调用时,频繁的创建对象、造成新生代空间不足,频繁的触发新生代垃圾回收。
比如秒杀接口的调用,线上注意合理分配新生代大小,默认新生代与老年代比例为1:2,一些特定的场景也可以适当调整占比,以减少新生代垃圾回收的次数,通过jstat观察垃圾回收的情况,当新生代回收次数特别多,老年代特别少,可以适当进行调整。转载地址:http://mllrb.baihongyu.com/