Peter,


Objects.requireNonNull() shows zero overhead here.

I guess the main question is whether Objects.requireNonNull(this) behavior in the former test is a result of chance and current Hotspot behavior or is it somehow guaranteed by the spec.

I haven't looked into what actually happens in JIT-compilers on your benchmark, but I'm surprised it works at all.

So, here's why Objects.requireNonNull() keeps the receiver alive in your test case.

JIT-compilers in HotSpot aggressively prune dead locals [1], but they do that based on method bytecode analysis [2] (and not on optimized IR). So, any usage of a local extends its live range, even if that usage is eliminated in generated code. It means an oop in that local will live past its last usage in generated code and all safepoints (in generated code) till last usage (on bytecode level) will enumerate the local it is held in.

If there were GC-only safepoints supported, then JITs could still prune unused locals from oop maps, but HotSpot doesn't have them and all safepoints in generated code keep full JVM state, so it's always possible to deoptimize at any one of them (and then run into the code which is eliminated in generated code).

If there are no safepoints till the end of the method, then nothing will keep the object alive. But there's no way for GC to collect it, since GCs rely on safepoints to mark thread stack. (That's why I mentioned GC-only safepoints earlier.)

As a conclusion: removing @DontInline on Reference.reachabilityFence() should eliminate most of the overhead (no call anymore, additional spill may be needed) and still keep it working. It's not guaranteed by JVMS, but at least should work on HotSpot JVM (in its current state).

So, nice discovery, Peter! :-) Want to file an RFE & fix it?

Best regards,
Vladimir Ivanov

[1] http://hg.openjdk.java.net/jdk/hs/file/45b6aae769cc/src/hotspot/share/opto/graphKit.cpp#l736

[2] http://hg.openjdk.java.net/jdk/hs/file/45b6aae769cc/src/hotspot/share/compiler/methodLiveness.cpp#l37

Explicit null check on the receiver is an easy target for elimination and should be effectively a no-op in generated code. (And that's what you observe with the benchmark!) Once the check is gone, nothing keeps receiver alive anymore (past the last usage).

So, I'd say such behavior it's a matter of chance in your case and can't be relied on in general. And definitely not something guaranteed by JVMS.

Best regards,
Vladimir Ivanov

Reply via email to