LinuxPerfAsmProfiler 显示 Java 8 的 Java 代码对应程序集热点,但不显示 Java 14

Ser*_*nov 5 java performance assembly profiling jmh

在调查与 Spring 的实例化相关的问题时org.springframework.util.ConcurrentReferenceHashMap(截至spring-core-5.1.3.RELEASE),我使用LinuxPerfAsmProfiler与 JMH 一起提供的工具来分析生成的程序集。

我只是运行这个

@Benchmark
public Object measureInit() {
  return new ConcurrentReferenceHashMap<>();
}
Run Code Online (Sandbox Code Playgroud)

JDK 8 上的基准测试允许识别不明显的热点之一:

  0.61%        0x00007f32d92772ea: lock addl $0x0,(%rsp)     ;*putfield count
                                                             ; - org.springframework.util.ConcurrentReferenceHashMap$Segment::&lt;init&gt;@11 (line 476)
                                                             ; - org.springframework.util.ConcurrentReferenceHashMap::&lt;init&gt;@141 (line 184)
 15.81%        0x00007f32d92772ef: mov    0x60(%r15),%rdx
Run Code Online (Sandbox Code Playgroud)

这对应于对 volatile 字段的不必要的默认值分配:

  0.61%        0x00007f32d92772ea: lock addl $0x0,(%rsp)     ;*putfield count
                                                             ; - org.springframework.util.ConcurrentReferenceHashMap$Segment::&lt;init&gt;@11 (line 476)
                                                             ; - org.springframework.util.ConcurrentReferenceHashMap::&lt;init&gt;@141 (line 184)
 15.81%        0x00007f32d92772ef: mov    0x60(%r15),%rdx
Run Code Online (Sandbox Code Playgroud)

Segment依次在以下构造函数的循环中实例化CCRHM

protected final class Segment extends ReentrantLock {
  private volatile int count = 0;
}
Run Code Online (Sandbox Code Playgroud)

所以这个指令很可能真的很热。组装的完整布局可以在我的要点中找到

然后我在 JDK 14 上运行相同的基准测试并再次使用LinuxPerfAsmProfiler,但现在我没有任何明确指向volatile int count = 0 捕获的程序集

寻找在前缀下lock addl $0x0分配的指令我发现了这个:0lock

  0.08%                          ?  0x00007f3717d46187:   lock addl $0x0,-0x40(%rsp)
 23.74%                          ?  0x00007f3717d4618d:   mov    0x120(%r15),%rbx
Run Code Online (Sandbox Code Playgroud)

这很可能是对应的,volatile int count = 0因为它遵循Segment的超类的构造函数调用ReentrantLock

  0.77%                          ?  0x00007f3717d46140:   movq   $0x0,0x18(%rax)              ;*new {reexecute=0 rethrow=0 return_oop=0}
                                 ?                                                            ; - java.util.concurrent.locks.ReentrantLock::&lt;init&gt;@5 (line 294)
                                 ?                                                            ; - org.springframework.util.ConcurrentReferenceHashMap$Segment::&lt;init&gt;@6 (line 484)
                                 ?                                                            ; - org.springframework.util.ConcurrentReferenceHashMap::&lt;init&gt;@141 (line 184)
  0.06%                          ?  0x00007f3717d46148:   mov    %r8,%rcx
  0.05%                          ?  0x00007f3717d4614b:   mov    %rax,%rbx
  0.03%                          ?  0x00007f3717d4614e:   shr    $0x3,%rbx
  0.74%                          ?  0x00007f3717d46152:   mov    %ebx,0xc(%r8)
  0.06%                          ?  0x00007f3717d46156:   mov    %rax,%rbx
  0.05%                          ?  0x00007f3717d46159:   xor    %rcx,%rbx
  0.02%                          ?  0x00007f3717d4615c:   shr    $0x14,%rbx
  0.72%                          ?  0x00007f3717d46160:   test   %rbx,%rbx
                             ?   ?  0x00007f3717d46163:   je     0x00007f3717d4617f
                             ?   ?  0x00007f3717d46165:   shr    $0x9,%rcx
                             ?   ?  0x00007f3717d46169:   movabs $0x7f370a872000,%rdi
                             ?   ?  0x00007f3717d46173:   add    %rcx,%rdi
                             ?   ?  0x00007f3717d46176:   cmpb   $0x8,(%rdi)
  0.00%                      ?   ?  0x00007f3717d46179:   jne    0x00007f3717d46509
  0.04%                      ?   ?  0x00007f3717d4617f:   movl   $0x0,0x14(%r8)
  0.08%                          ?  0x00007f3717d46187:   lock addl $0x0,-0x40(%rsp)
 23.74%                          ?  0x00007f3717d4618d:   mov    0x120(%r15),%rbx
Run Code Online (Sandbox Code Playgroud)

问题是我根本没有提到putfield count生成的程序集。

谁能解释一下为什么我看不到它?

Ser*_*nov 1

事实证明,您无法使用为 JDK 8 和 JDK 11 构建的 hsdis。为了完美匹配,您需要从 JDK 源构建 hsdis,然后构建 JDK 本身并在此临时构建上运行应用程序。

当我调查字符串构造函数中缺少边界检查消除时,这种方法对我来说非常有效?