我有一个jetty服务器在后端和转换protobufs frontend.proto->backend.proto和backend.proto->frontend.proto之间进行代理。
但是,当负载达到峰值时,第99个增加+10,而第99.9个增加+60。
我已经调查过了,延迟请求是由于GC疏散暂停引起的,我确信,这种暂停需要50-70ms,在山谷负载下每15秒运行一次,但在峰值负载下每3-5秒跳一次,持续时间相同。
当GC频率低于8-9秒时,99.9%的数据就会迅速上升,我可以同时看到慢速请求的调试日志和GC日志。
我已经和JProfiler,Yourkit和VisualVM进行了分析,发现:
-
Eden空间填满并触发GC暂停
-
-
所以大部分的物品在伊甸园已经过期了
-
这是有意义的,因为请求需要30-40毫秒,而且大多数对象的生存期都与请求的生存期相关联
-
我试过玩GCPauseMillis和Eden大小的游戏,但似乎没有什么不同,它从不少于50ms,而更大的Eden意味着频率更低,但停顿时间更长
我在这里看到两个选项:
-
在java protobuf中以某种方式重用对象创建:似乎是不可能的,阅读了大量的文章和邮件,并没有这样设置,他们只是说“java对象分配非常有效,它应该能够处理正在创建的许多对象”,虽然这是真的,但是相关的GC开销正在扼杀我的99.9
-
让GC更频繁地运行,比如每秒运行一次,以减少收集时间,这样它将停止更多的请求,但会缩短时间:我一直在使用gcmaxmlis和Eden大小,但似乎无法将其降低
我把gc日志上传到
gc_log
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
GC详细信息:
-XX:CICompilerCount=12 -XX:ConcGCThreads=5 -XX:ErrorFile=/home/y/var/crash/hs_err_pid%p.log -XX:+FlightRecorder -XX:G1HeapRegionSize=4194304 -XX:GCLogFileSize=4194304 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/y/logs/yjava_jetty -XX:InitialHeapSize=12884901888 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=12884901888 -XX:MaxNewSize=7730102272 -XX:MinHeapDeltaBytes=4194304 -XX:NumberOfGCLogFiles=10 -XX:+ParallelRefProcEnabled -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UnlockCommercialFeatures -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:+UseGCLogFileRotation