JVM多线程程序性能无法随CPU核心数线性扩展问题排查
问题解答
1、无显式共享下的隐式争用确实存在,争用发生在硬件、JVM多个层级
你观察到的性能反伸缩不是业务代码的显式锁或共享变量导致的,隐式争用真实存在,主要出现在以下层级:
- 硬件资源层
- 你使用的vCPU是超线程产出的逻辑核,同属一个物理核的两个逻辑核完全共享浮点运算单元(FPU)、L1/L2缓存、指令执行端口,你的测试负载是纯浮点除法,属于典型的FPU密集型任务,没有多余的硬件资源给第二个逻辑核用,核心数越多(尤其是超线程出来的逻辑核越多),同物理核内的资源争抢越严重,单线程执行效率直接下降。
- 所有核心共享L3缓存、内存总线带宽,核心数越高,每个核心能分到的缓存、带宽配额越低,访存等待时间变长。
- 多核心下的缓存一致性协议开销:哪怕是线程私有的变量,如果不同线程的高频访问变量落在同一个64字节缓存行上,会触发伪共享问题,缓存行会在多个核心的缓存间反复失效、同步,带来非常高的额外开销。
- CPU频率墙:核心数越少、负载越低时,CPU可以跑到更高的睿频频率;当所有核心全负载时,受功耗、温度限制,CPU会自动降频,单核心的计算能力会出现明显下降。
- JVM运行时层
即使业务代码无共享,JVM本身仍存在少量全局共享的逻辑:比如安全点同步(GC、类重定义等操作需要所有线程进入安全点,线程越多等待同步的时间越长)、TLAB耗尽后更新堆全局分配指针的CAS操作、JIT编译器的编译队列排队、GC的全局标记停顿等,这些逻辑没有显式锁,但会随线程/核心数上升带来额外开销。
2、无法线性扩展是全行业普遍现象
不存在能随核心数做到完美线性扩展的应用程序,这是由阿姆达尔定律和硬件物理限制决定的:
- 任何程序都存在无法并行的串行逻辑,哪怕串行部分只占总执行时间的1%,在64核规模下扩展效率就会跌到60%以下。
- 硬件层面的共享资源(FPU、缓存、内存带宽、频率墙)是物理限制,所有运行在CPU上的程序都要受这个约束,和是不是JVM应用没有关系。
- 对纯计算密集型负载来说,行业内通用的扩展效率参考是:物理核规模下做到70%80%的扩展效率(即16物理核性能是8物理核的1.41.6倍)就属于优化水平极高的水平;如果开了超线程跑纯计算任务,扩展效率跌到30%以下甚至负优化都是非常常见的情况。你之前的测试结果差,很大原因是把超线程的逻辑核当成了独立物理核来算扩展比,本身预期就不符合硬件特性。
3、可落地的优化手段
要接近线性扩展的性能,可以从以下几个方向调整:
- 先修正测试基准的合理性
- 统计口径换成墙钟时间:即记录第一个线程提交到最后一个线程返回结果的总耗时,不要把每个线程的单独执行时间累加,这个统计方式算的是CPU总耗时,会把调度、争用的开销重复计算,无法反映真实的吞吐能力。
- 纯计算密集型任务的线程数和物理核心数保持一致即可,不要用超线程出来的逻辑核数配线程数,超线程对FPU密集型负载基本是负收益,有条件可以直接在BIOS层面关闭超线程测试,扩展效率会有明显提升。
- 增加预热环节:正式测试前先跑3~5轮相同负载,让JIT编译器把热点代码编译为本地机器码,避免测试过程中动态编译带来的性能波动。
- 硬件资源层面优化
- 开启JVM的NUMA感知:加启动参数
-XX:+UseNUMA,让线程优先分配当前NUMA节点的本地内存,减少跨NUMA访存的延迟。 - 做线程绑核:通过taskset或JVM的绑核参数,把计算线程固定绑定到独立的物理核上,避免操作系统调度线程在不同核心间漂移导致缓存失效,也避免两个计算线程跑到同一个物理核的两个逻辑核上抢资源。
- 规避伪共享:对线程私有的高频访问变量,用
@jdk.internal.vm.annotation.Contended注解做缓存行填充,同时加启动参数-XX:-RestrictContended开启注解支持,避免不同线程的变量落在同一个缓存行触发一致性同步。
- 开启JVM的NUMA感知:加启动参数
- JVM参数调优
- 调大TLAB尺寸:加参数
-XX:TLABSize=1m,减少线程运行过程中申请新TLAB块的次数,降低全局分配指针的CAS争用。 - 关闭高并发下无收益的偏向锁:加参数
-XX:-UseBiasedLocking,减少高并发下偏向锁撤销的额外开销。 - 选用低停顿GC:比如ZGC/Shenandoah,适当增大堆内存,降低GC频率和全局停顿时间。
- 调大TLAB尺寸:加参数
- 代码层面优化
- 对核心计算循环做手动循环展开,减少循环分支判断的开销,提升CPU指令流水线的利用率。
- 核心循环内避免调用任何可能触发阻塞、全局操作的方法,你当前代码把时间统计放到循环外是正确的。
- 保持当前ThreadLocalRandom的用法,不要退回到共享Random实例,避免随机数种子的CAS争用。
内容的提问来源于stack exchange,提问作者Shubham Chaurasia
相关产品推荐
相关产品推荐

