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::<init>@11 (line 476)
; - org.springframework.util.ConcurrentReferenceHashMap::<init>@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::<init>@11 (line 476)
; - org.springframework.util.ConcurrentReferenceHashMap::<init>@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::<init>@5 (line 294)
? ; - org.springframework.util.ConcurrentReferenceHashMap$Segment::<init>@6 (line 484)
? ; - org.springframework.util.ConcurrentReferenceHashMap::<init>@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生成的程序集。
谁能解释一下为什么我看不到它?
事实证明,您无法使用为 JDK 8 和 JDK 11 构建的 hsdis。为了完美匹配,您需要从 JDK 源构建 hsdis,然后构建 JDK 本身并在此临时构建上运行应用程序。
当我调查字符串构造函数中缺少边界检查消除时,这种方法对我来说非常有效?。