/* * Copyright (c) 2014, Oracle America, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Oracle nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.openjdk; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import java.util.Objects; import java.util.concurrent.TimeUnit; @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Fork(3) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) public class NullChecks { /* TL;DR: Fear not, my little friends, use Objects.requireNonNull. Stop using these obfuscating Object.getClass() checks, those rely on non-related intrinsic performance, potentially not available everywhere. Runs are done on i5-4210U, 1.7 GHz, Linux x86_64, JDK 8u40 EA. The explanations are derived from studying the generated code ("-prof perfasm" is your friend here), the disassembly is skipped for brevity. ------ Out of box, C2 compiled: Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 0.588 ± 0.015 ns/op NullChecks.objectGetClass avgt 25 0.594 ± 0.009 ns/op NullChecks.objectsNonNull avgt 25 0.598 ± 0.014 ns/op Object.getClass() is intrinsified. Objects.requireNonNull is perfectly inlined. ------ -XX:-Inline, C2 compiled, but no inlining: Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 2.450 ± 0.020 ns/op NullChecks.objectGetClass avgt 25 2.450 ± 0.010 ns/op NullChecks.objectsNonNull avgt 25 4.433 ± 0.046 ns/op This emulates the inlining failure. Highly unlikely in practice, given Objects.requireNonNull is a tiny static method. (It is a JDK bug otherwise, and should be dealt with, all due cruelty and mayhem included) Object.getClass ignores the inline settings, and still intrinsifies. Objects.requireNonNull pays the cost of additional call. All methods also pay the cost for non-inlined @Benchmark method. ------ -XX:TieredStopAtLevel=1, C1 compiled Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 0.757 ± 0.002 ns/op NullChecks.objectGetClass avgt 25 1.035 ± 0.007 ns/op NullChecks.objectsNonNull avgt 25 0.761 ± 0.008 ns/op Object.getClass also intrinsifies, but not that efficiently. Objects.requireNonNull inlines and optimizes neatly. ------ -XX:TieredStopAtLevel=1 -XX:-Inline, C1 compiled, no inline Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 3.233 ± 0.024 ns/op NullChecks.objectGetClass avgt 25 33.877 ± 0.640 ns/op NullChecks.objectsNonNull avgt 25 5.045 ± 0.132 ns/op Object.getClass had failed to intrinsify, since C1 stops inlining intrinsics with -XX:-Inline -- it calls native jni_GetObjectClass. Everything else is in accordance of C2'ish -XX:-Inline. ------ -Xint, interpreter mode Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 100.733 ± 2.861 ns/op NullChecks.objectGetClass avgt 25 161.147 ± 2.355 ns/op NullChecks.objectsNonNull avgt 25 179.558 ± 5.490 ns/op Object.getClass calls into VM. Remarkably, it is still on par with Objects.requireNonNull, allegedly because of this: https://bugs.openjdk.java.net/browse/JDK-8072070 ...working around with: -Xint -XX:StackShadowPages=1, interpreter mode Benchmark Mode Cnt Score Error Units NullChecks.branch avgt 25 30.295 ± 0.090 ns/op NullChecks.objectGetClass avgt 25 83.332 ± 1.181 ns/op NullChecks.objectsNonNull avgt 25 50.637 ± 0.774 ns/op Clearly shows the overheads of calling into native getClass. ------ */ private Object o; @Setup public void setup() { o = new NullChecks(); } @Benchmark public void objectGetClass() { o.getClass(); } @Benchmark public void objectsNonNull() { Objects.requireNonNull(o); } @Benchmark public void branch() { if (o == null) { throw new NullPointerException(); } } }