From 986ad2da2ba87fbfef2bb5d40f203036f97a9e33 Mon Sep 17 00:00:00 2001 From: sharan bharmshetty Date: Thu, 17 Sep 2026 22:39:33 +0530 Subject: [PATCH 1/2] Avoid parsing nested jar URLs for cached resource lookups Construct JarFileUrlKey directly from already-parsed URL specification to avoid unnecessary URL construction and parsing on cache hits. A URL is only created on a cache miss when the JarFile needs to be instantiated. This eliminates redundant object allocation in the hot path when frameworks probe for optional classes or package-info metadata across a large executable JAR. See gh-51827 Signed-off-by: sharanrenukeshbharmshetty --- .../net/protocol/jar/JarFileUrlKey.java | 8 +++++++ .../net/protocol/jar/JarUrlConnection.java | 16 +++++++++---- .../loader/net/protocol/jar/UrlJarFiles.java | 19 ++++++++++++++- .../net/protocol/jar/JarFileUrlKeyTests.java | 23 +++++++++++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-) 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 cae978d133c..44df4bc1d9d 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,14 @@ final class JarFileUrlKey { this.runtimeRef = "runtime".equals(url.getRef()); } + JarFileUrlKey(String protocol, String host, int port, String file, boolean runtimeRef) { + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + this.runtimeRef = runtimeRef; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java index ba6521de5de..0b78cc298e1 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java @@ -337,13 +337,19 @@ final class JarUrlConnection extends java.net.JarURLConnection { int separator = spec.indexOf("!/"); boolean specHasEntry = (separator != -1) && (separator + 2 != spec.length()); if (specHasEntry) { - URL jarFileUrl = new URL(spec.substring(0, separator)); - if ("runtime".equals(url.getRef())) { - jarFileUrl = new URL(jarFileUrl, "#runtime"); + String jarFileSpec = spec.substring(0, separator); + boolean runtimeRef = "runtime".equals(url.getRef()); + JarFileUrlKey urlKey = new JarFileUrlKey("jar", "", -1, jarFileSpec, runtimeRef); + JarFile jarFile = jarFiles.getCached(urlKey); + if (jarFile == null) { + URL jarFileUrl = new URL(jarFileSpec); + if (runtimeRef) { + jarFileUrl = new URL(jarFileUrl, "#runtime"); + } + jarFile = jarFiles.getOrCreate(true, jarFileUrl); + jarFiles.cacheIfAbsent(true, jarFileUrl, jarFile); } String entryName = UrlDecoder.decode(spec.substring(separator + 2)); - JarFile jarFile = jarFiles.getOrCreate(true, jarFileUrl); - jarFiles.cacheIfAbsent(true, jarFileUrl, jarFile); if (!hasEntry(jarFile, entryName)) { return notFoundConnection(jarFile.getName(), entryName); } 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 e2dbb3a29e9..c848d62e1ca 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 @@ -81,6 +81,15 @@ class UrlJarFiles { return this.cache.get(jarFileUrl); } + /** + * Return the cached {@link JarFile} if available. + * @param urlKey the jar file URL key + * @return the cached jar or {@code null} + */ + JarFile getCached(JarFileUrlKey urlKey) { + return this.cache.get(urlKey); + } + /** * Cache the given {@link JarFile} if caching can be used and there is no existing * entry. @@ -154,7 +163,15 @@ class UrlJarFiles { * @return the cached {@link JarFile} or {@code null} */ JarFile get(URL jarFileUrl) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); + return get(new JarFileUrlKey(jarFileUrl)); + } + + /** + * Get a {@link JarFile} from the cache given a jar file URL key. + * @param urlKey the jar file URL key + * @return the cached {@link JarFile} or {@code null} + */ + JarFile get(JarFileUrlKey urlKey) { synchronized (this) { return this.jarFileUrlToJarFile.get(urlKey); } diff --git a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java index 575630819aa..34bb217870b 100644 --- a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java +++ b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java @@ -101,4 +101,27 @@ class JarFileUrlKeyTests { return new JarFileUrlKey(new URL(spec)); } + @Test + void directKeyEqualsUrlKey() throws Exception { + JarFileUrlKey fromUrl = key("jar:nested:/my.jar/!mynested.jar!/my/path"); + JarFileUrlKey direct = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", false); + assertThat(direct).isEqualTo(fromUrl); + assertThat(direct.hashCode()).isEqualTo(fromUrl.hashCode()); + } + + @Test + void directKeyWithRuntimeRefEqualsUrlKey() throws Exception { + JarFileUrlKey fromUrl = key("jar:nested:/my.jar/!mynested.jar!/my/path#runtime"); + JarFileUrlKey direct = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", true); + assertThat(direct).isEqualTo(fromUrl); + assertThat(direct.hashCode()).isEqualTo(fromUrl.hashCode()); + } + + @Test + void directKeyWithRuntimeRefNotEqualToKeyWithout() { + JarFileUrlKey k1 = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", true); + JarFileUrlKey k2 = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", false); + assertThat(k1).isNotEqualTo(k2); + } + } From 8fc6a01b28a2648d55015b789b6cb65afa64e867 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 28 Sep 2026 18:52:31 -0700 Subject: [PATCH 2/2] Polish 'Avoid parsing nested jar URLs for cached resource lookups' See gh-51827 --- .../net/protocol/jar/JarUrlConnection.java | 16 ++----- .../loader/net/protocol/jar/UrlJarFiles.java | 48 +++++++++++++------ .../net/protocol/jar/JarFileUrlKeyTests.java | 1 + 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java index 0b78cc298e1..5879a914163 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java @@ -45,6 +45,7 @@ import org.springframework.boot.loader.net.util.UrlDecoder; * @author Phillip Webb * @author Andy Wilkinson * @author Rostyslav Dudka + * @author Sharan Bharmshetty */ final class JarUrlConnection extends java.net.JarURLConnection { @@ -335,20 +336,11 @@ final class JarUrlConnection extends java.net.JarURLConnection { String spec = url.getFile(); if (spec.startsWith("nested:")) { int separator = spec.indexOf("!/"); - boolean specHasEntry = (separator != -1) && (separator + 2 != spec.length()); - if (specHasEntry) { + boolean specHasJarEntry = (separator != -1) && (separator + 2 != spec.length()); + if (specHasJarEntry) { String jarFileSpec = spec.substring(0, separator); boolean runtimeRef = "runtime".equals(url.getRef()); - JarFileUrlKey urlKey = new JarFileUrlKey("jar", "", -1, jarFileSpec, runtimeRef); - JarFile jarFile = jarFiles.getCached(urlKey); - if (jarFile == null) { - URL jarFileUrl = new URL(jarFileSpec); - if (runtimeRef) { - jarFileUrl = new URL(jarFileUrl, "#runtime"); - } - jarFile = jarFiles.getOrCreate(true, jarFileUrl); - jarFiles.cacheIfAbsent(true, jarFileUrl, jarFile); - } + JarFile jarFile = jarFiles.getOrCreateAndCache(jarFileSpec, runtimeRef); String entryName = UrlDecoder.decode(spec.substring(separator + 2)); if (!hasEntry(jarFile, entryName)) { return notFoundConnection(jarFile.getName(), entryName); 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 c848d62e1ca..6bc0590c019 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 @@ -31,6 +31,7 @@ import java.util.jar.JarFile; * instances. * * @author Phillip Webb + * @author Sharan Bharmshetty */ class UrlJarFiles { @@ -53,6 +54,20 @@ class UrlJarFiles { this.factory = factory; } + JarFile getOrCreateAndCache(String jarFileSpec, boolean runtimeRef) throws IOException { + JarFileUrlKey jarFileUrlKey = new JarFileUrlKey("jar", "", -1, jarFileSpec, runtimeRef); + JarFile jarFile = this.cache.get(jarFileUrlKey); + if (jarFile == null) { + URL jarFileUrl = new URL(jarFileSpec); + if (runtimeRef) { + jarFileUrl = new URL(jarFileUrl, "#runtime"); + } + jarFile = this.factory.createJarFile(jarFileUrl, this::onClose); + this.cache.putIfAbsent(jarFileUrlKey, jarFile, jarFileUrl); + } + return jarFile; + } + /** * Get an existing {@link JarFile} instance from the cache, or create a new * {@link JarFile} instance that can be {@link #cacheIfAbsent(boolean, URL, JarFile) @@ -81,15 +96,6 @@ class UrlJarFiles { return this.cache.get(jarFileUrl); } - /** - * Return the cached {@link JarFile} if available. - * @param urlKey the jar file URL key - * @return the cached jar or {@code null} - */ - JarFile getCached(JarFileUrlKey urlKey) { - return this.cache.get(urlKey); - } - /** * Cache the given {@link JarFile} if caching can be used and there is no existing * entry. @@ -168,12 +174,12 @@ class UrlJarFiles { /** * Get a {@link JarFile} from the cache given a jar file URL key. - * @param urlKey the jar file URL key + * @param jarFileUrlKey the jar file URL key * @return the cached {@link JarFile} or {@code null} */ - JarFile get(JarFileUrlKey urlKey) { + JarFile get(JarFileUrlKey jarFileUrlKey) { synchronized (this) { - return this.jarFileUrlToJarFile.get(urlKey); + return this.jarFileUrlToJarFile.get(jarFileUrlKey); } } @@ -197,11 +203,23 @@ class UrlJarFiles { * they were already there */ boolean putIfAbsent(URL jarFileUrl, JarFile jarFile) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); + return putIfAbsent(new JarFileUrlKey(jarFileUrl), jarFile, jarFileUrl); + } + + /** + * Put the given jar file URL and jar file into the cache if they aren't already + * there. + * @param jarFileUrlKey the jar file URL key + * @param jarFileUrl the jar file URL + * @param jarFile the jar file + * @return {@code true} if the items were added to the cache or {@code false} if + * they were already there + */ + boolean putIfAbsent(JarFileUrlKey jarFileUrlKey, JarFile jarFile, URL jarFileUrl) { synchronized (this) { - JarFile cached = this.jarFileUrlToJarFile.get(urlKey); + JarFile cached = this.jarFileUrlToJarFile.get(jarFileUrlKey); if (cached == null) { - this.jarFileUrlToJarFile.put(urlKey, jarFile); + this.jarFileUrlToJarFile.put(jarFileUrlKey, jarFile); this.jarFileToJarFileUrl.put(jarFile, jarFileUrl); return true; } diff --git a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java index 34bb217870b..1628b0ad084 100644 --- a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java +++ b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java @@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link JarFileUrlKey}. * * @author Phillip Webb + * @author Sharan Bharmshetty */ class JarFileUrlKeyTests {