Java参数调优有哪些技巧?

话题来源: Fabric服务端高并发优化方案

你知道吗,每次看到Java应用因为参数配置不当而性能拉胯,我都忍不住想吐槽——明明稍微调整几个参数就能解决的事,非要搞得那么复杂。去年我们团队接手一个高并发项目,刚开始JVM参数都是默认配置,结果上线第一天就直接内存溢出。后来经过一轮轮调优测试,才发现Java参数调优真是个技术活,既要有理论支撑,又得靠实践经验。

内存参数:别让内存成为瓶颈

内存设置绝对是调优的重头戏!我见过太多人把-Xmx设得过大,结果反而导致GC停顿时间飙升。其实这里有个黄金法则:堆内存不要超过物理内存的75%,而且-Xmx和-Xms最好设置相同值,这样能避免运行时动态调整带来的开销。比如我们项目用的是G1垃圾回收器,参数这样配效果就很好:

-Xmx8G -Xms8G 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=200 
-XX:ParallelGCThreads=8 
-XX:ConcGCThreads=4

看到没?MaxGCPauseMillis设成200毫秒是个甜点值,既能保证吞吐量,又不会让GC停顿太明显。不过要提醒的是,这个值不是越小越好,设得太小反而会导致GC更频繁。

垃圾回收器选择:因地制宜才是王道

说到垃圾回收器,现在主流的就是G1和ZGC了。G1适合大多数场景,特别是那种内存不太大(比如64G以内)的应用。但如果你用的是超大堆内存,比如128G往上,那我强烈推荐ZGC,它的停顿时间能控制在10毫秒以内!不过ZGC对JDK版本有要求,得用JDK11以上。

记得有次给一个电商项目做调优,他们原来的CMS回收器老是发生并发模式失败,换成G1之后,Full GC直接从每天几次降到零!这种体验,啧啧,真的是立竿见影。

线程池参数:别让线程成为性能杀手

线程池参数调优经常被忽略,但其实特别重要!我们之前有个服务,线程池核心线程数设得太大,结果上下文切换开销直接把CPU占满了。后来根据实际业务特点重新计算,发现核心线程数设置在CPU核数的1.5倍最合适。

队列长度也是个坑,用无界队列很容易导致内存溢出。我们现在都改用有界队列,配合合适的拒绝策略,比如CallerRunsPolicy,虽然会稍微影响性能,但至少系统不会崩掉。

监控与诊断:没有数据就是瞎调优

调优最怕什么?拍脑袋做决定!我们现在每个服务都配了JMX监控,用VisualVM或者JDK自带的jstat实时查看GC情况。特别是老年代使用率和GC频率,这两个指标一有异常,立马就能发现。

有次我们发现某个服务的Young GC特别频繁,一查代码才发现是有人在循环里疯狂创建临时对象。这种问题不靠监控数据,光凭猜根本找不出来。

说到底,Java参数调优既是一门科学,也是一门艺术。需要不断测试、观察、调整,找到最适合自己业务场景的那个甜点。你们在调优过程中都遇到过哪些坑?欢迎一起来聊聊!

评论