diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index fb697e1df059d..6f2bd440349ca 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -110,6 +110,10 @@ public boolean isAssignableFrom(ClassDesc thisClass, ClassDesc fromClass) { public static final class CachedClassHierarchyResolver implements ClassHierarchyResolver { + //this instance should never appear in the cache nor leak out + private static final ClassHierarchyResolver.ClassHierarchyInfo NOPE = + new ClassHierarchyResolver.ClassHierarchyInfo(null, false, null); + private final Function streamProvider; private final Map resolvedCache; @@ -124,9 +128,11 @@ public CachedClassHierarchyResolver(Function classStream // empty ClInfo is stored in case of an exception to avoid repeated scanning failures @Override public ClassHierarchyResolver.ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { - var res = resolvedCache.get(classDesc); - //additional test for null value is important to avoid repeated resolution attempts - if (res == null && !resolvedCache.containsKey(classDesc)) { + //using NOPE to distinguish between null value and non-existent record in the cache + //this code is on JDK bootstrap critical path, so cannot use lambdas here + var res = resolvedCache.getOrDefault(classDesc, NOPE); + if (res == NOPE) { + res = null; var ci = streamProvider.apply(classDesc); if (ci != null) { try (var in = new DataInputStream(new BufferedInputStream(ci))) {