Would like an explanation on the difference between local and static VarHandles
Hello everyone o/
I currently try to get into VarHandles and MethodHandles because the Unsafe API was deprecated in JDK 23 and marked for removal.
While I was doing some tests I noticed that VarHandles that were defined as local constants inside a method were notably slower than VarHandles that were defined as static constants.
I then did a quick benchmark where I filled long arrays with random values and moved those values into appropriately sized byte arrays (i.e. with 8x the length) using these four approaches:
- use a static
VarHandleByteArrayAsLongs.ArrayHandle - use a local
VarHandleByteArrayAsLongs.ArrayHandle - use a static
Unsafereference - use a local
Unsafereference
You can see the code here: https://pastebin.com/7ZbZKcgu
I am using the OpenJDK 26.
This is the output from the program. The units are nano seconds. I removed the memory access warnings from calling sun.misc.Unsafe::putLong. I did 2500 warmup iterations, 2500 benchmark iterations, the long arrays had a length of 1 << 21 (2097152) and the byte arrays had a length of 16777216
useStaticVarHandle
min=776915, max=11360330, median=1998262, average=2072531.6216, standardDeviation=459769.1256223566
useLocalVarHandle
min=4220452, max=10941397, median=4289771, average=4334655.0888, standardDeviation=351202.1747500401
useStaticUnsafe
min=872193, max=5504646, median=1950182, average=2072433.7144, standardDeviation=337598.53310213424
useLocalUnsafe
min=860962, max=6200399, median=1944070, average=2046593.6408, standardDeviation=337197.05616648745
As you can see, the static VarHandle performed similar to the static and local Unsafe. However, the VarHandle that was defined inside a method performed worse.
Could anyone explain to me why that is or nudge me towards resources that explain it? I am rather confused because VarHandleByteArrayAsLongs.ArrayHandle, just like sun.misc.Unsafe, internally uses the jdk.internal.misc.Unsafe singleton. The Java compiler should also see that the call to get the VarHandle inside the method is always the same. Why would the static instance of the Varhandle be better optimized than the local instance inside the method? Do I need a specific setting to force more aggressive optimizations or is this an intrinsic "issue" of VarHandles?
I hope I was able to phrase my questions well enough. If you need any further information I'll be happy to provide. Thanks in advance!