diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java index cae978d133c8..45932d35f91e 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java @@ -44,6 +44,21 @@ final class JarFileUrlKey { this.runtimeRef = "runtime".equals(url.getRef()); } + /** + * Return a string representation of this key suitable for use as a + * {@code HashMap} key. Unlike {@code URL.toString()}, this + * method does not trigger DNS lookups. + * @return a unique string key for this URL + */ + String toKeyString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.protocol).append("://").append(this.host).append(':').append(this.port).append(this.file); + if (this.runtimeRef) { + sb.append("#runtime"); + } + return sb.toString(); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -68,4 +83,4 @@ private boolean equalsIgnoringCase(String s1, String s2) { return (s1 == s2) || (s1 != null && s1.equalsIgnoreCase(s2)); } -} +} \ No newline at end of file diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java index e2dbb3a29e95..591220d347fc 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java @@ -140,11 +140,13 @@ void clearCache() { } /** - * Internal cache. + * Internal cache. Uses {@code String} keys in the underlying map to avoid + * polluting the JVM's shared {@code HashMap.getNode} type profile with a + * custom key type. See gh-51141. */ private static final class Cache { - private final Map jarFileUrlToJarFile = new HashMap<>(); + private final Map jarFileUrlToJarFile = new HashMap<>(); private final Map jarFileToJarFileUrl = new HashMap<>(); @@ -154,9 +156,8 @@ private static final class Cache { * @return the cached {@link JarFile} or {@code null} */ JarFile get(URL jarFileUrl) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); synchronized (this) { - return this.jarFileUrlToJarFile.get(urlKey); + return this.jarFileUrlToJarFile.get(new JarFileUrlKey(jarFileUrl).toKeyString()); } } @@ -180,11 +181,11 @@ URL get(JarFile jarFile) { * they were already there */ boolean putIfAbsent(URL jarFileUrl, JarFile jarFile) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); synchronized (this) { - JarFile cached = this.jarFileUrlToJarFile.get(urlKey); + String key = new JarFileUrlKey(jarFileUrl).toKeyString(); + JarFile cached = this.jarFileUrlToJarFile.get(key); if (cached == null) { - this.jarFileUrlToJarFile.put(urlKey, jarFile); + this.jarFileUrlToJarFile.put(key, jarFile); this.jarFileToJarFileUrl.put(jarFile, jarFileUrl); return true; } @@ -200,7 +201,7 @@ void remove(JarFile jarFile) { synchronized (this) { URL removedUrl = this.jarFileToJarFileUrl.remove(jarFile); if (removedUrl != null) { - this.jarFileUrlToJarFile.remove(new JarFileUrlKey(removedUrl)); + this.jarFileUrlToJarFile.remove(new JarFileUrlKey(removedUrl).toKeyString()); } } } @@ -214,4 +215,4 @@ void clear() { } -} +} \ No newline at end of file