Search code examples
javaintrinsicsjvm-hotspot

Intrinsic candidate static method reference disappears after a while?


I have the following Comparator implemenation:

private static final class ValueComparator<Value_, Point_ extends Comparable<Point_>> implements Comparator<Value_> {

    private final Function<Value_, Point_> indexFunction;

    public ValueComparator(Function<Value_, Point_> indexFunction) {
        this.indexFunction = Objects.requireNonNull(indexFunction);
    }

    @Override
    public int compare(Value_ o1, Value_ o2) {
        if (o1 == o2) {
            return 0;
        }
        Point_ point1 = indexFunction.apply(o1);
        Point_ point2 = indexFunction.apply(o2);
        if (point1 == point2) {
            return 0;
        }
        int comparison = point1.compareTo(point2);
        if (comparison != 0) {
            return comparison;
        }
        // Identity Hashcode for duplicate protection; we must always include duplicates.
        // Ex: two different games on the same time slot
        int identityHashCode1 = System.identityHashCode(o1);
        int identityHashCode2 = System.identityHashCode(o2);
        return Integer.compare(identityHashCode1, identityHashCode2);
    }
}

This code runs for a while, and then suddenly throws an NPE - indexFunction somehow becomes null, even though it was checked to be non-null in the constructor.

Obviously, this was bugging me, so I went looking, and I found that the indexFunction is Math::abs, a reference to a static method. And java.lang.Math.abs(int) is @HotSpotIntrinsicCandidate.

Does that mean that HotSpot has, at some point, intrinsified the method and therefore the method reference became invalid? How do I protect myself against this sort of thing?

$ java -version
openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment Temurin-11.0.12+7 (build 11.0.12+7)
OpenJDK 64-Bit Server VM Temurin-11.0.12+7 (build 11.0.12+7, mixed mode)

Solution

  • Turns out the answer is much simpler and much less sensational. The argument to the indexFunction was null, and with the function expecting int, the NPE came from unboxing. In this case, the NPE still points to the indexFunction, as opposed to some code inside of it, which initially led me down a totally wrong path.

    Apparently I wasn't as thorough in checking my inputs as I had thought. Rookie mistake, case closed.