The implicit null check when doing string.toString() is handled in a reasonably graceful way, but when doing explicit null checks and throwing a NPE in java.util.Objects.requireNonNull, the performance degenerates a lot more drastically. Since Objects.requireNonNull is used extensively in the JDK we should try to achieve similar performance characteristics
Results using attached JMH[1] microbenchmark:
0% null values: performance on par
1% null values: implicit -17%, requireNonNull -82%
java -jar target/microbenchmark.jar -f 3 -wi 8 -i 2 -t 2 ".*NullCheckBench.*"
Benchmark Mode Samples Mean Mean error Units
s.m.NullCheckBench.nullToString0p thrpt 6 656137.726 38047.328 ops/ms
s.m.NullCheckBench.nullToString1p thrpt 6 543812.134 18287.248 ops/ms
s.m.NullCheckBench.nullToString1pExtraStack thrpt 6 541348.560 10447.750 ops/ms
s.m.NullCheckBench.nullToString10p thrpt 6 214927.628 11711.270 ops/ms
s.m.NullCheckBench.nullToString50p thrpt 6 55391.524 2224.665 ops/ms
s.m.NullCheckBench.nullToString100p thrpt 6 31790.624 3407.311 ops/ms
s.m.NullCheckBench.requireNonNull0p thrpt 6 667901.165 17983.635 ops/ms
s.m.NullCheckBench.requireNonNull1p thrpt 6 116286.386 8363.924 ops/ms
s.m.NullCheckBench.requireNonNull1pExtraStack thrpt 6 114705.760 3837.641 ops/ms
s.m.NullCheckBench.requireNonNull10p thrpt 6 14871.953 1891.190 ops/ms
s.m.NullCheckBench.requireNonNull50p thrpt 6 3027.838 211.838 ops/ms
s.m.NullCheckBench.requireNonNull100p thrpt 6 1494.453 220.459 ops/ms
Explanation: ..0p = 0% null values in test data, 1p = 1% and so forth. For ..ExtraStack I added 3 layers of method calls to increase stack depth to rule out that the extra stack trace entry in the requireNonNull case was adding a lot of extra overhead.
[1] http://openjdk.java.net/projects/code-tools/jmh/