This issue is basically the same as I described in this issue: Related issue

First of all, thank you for making a fix so fast. I have tested the fix and it does not fix the performance issue of the jar class loader on windows. I investigated a bit further. So the fix makes sure the dns lookup is not executed when there is a host. However in my applications all my URL's do not have a host filled in JarFileUrlKey. So the URLS that are saved in the cache all look like this: - protocol: "nested" - host: "" - port: -1 - file: "/C:/work/projects/spring-boot-demo/target/test.jar/!BOOT-INF/lib/spring-boot-3.3.13.jar" - ref: "null"

I have tested what code in the equals function makes the class loading so slow. It seems to be the synchronized InetAddress getHostAddress() function in java.net.URL:

synchronized InetAddress getHostAddress() {
        if (hostAddress != null) {
            return hostAddress;
        }

        if (host == null || host.isEmpty()) {
            return null;
        }
        try {
            hostAddress = InetAddress.getByName(host);
        } catch (UnknownHostException | SecurityException ex) {
            return null;
        }
        return hostAddress;
}

So for the cache the hostAddress is always null and the host is empty. So it only passes the 2 statements, enters the second one and returns null. The function is synchronized, but it should not have a big impact on performance in this case.

I ran some tests to check performance on the URL class in windows with OpenJDK 17. If I call the hashCode() function on 10,000 URL objects (without host filled) it takes 470ms. When I override the getHostAddress in the UrlStreamHandler with the following code:

@Override
protected InetAddress getHostAddress(URL u) {
      return getHostAddressSynchronized(u);
}

protected InetAddress getHostAddressSynchronized(URL u) {
     synchronized (u) {
          if (u.getHost() == null || u.getHost().isEmpty()) {
               return null;
          }
          try {
               return InetAddress.getByName(u.getHost());
          } catch (UnknownHostException | SecurityException ex) {
               return null;
          }
     }
}

With this override, it only takes 6ms for 10000 hashCode calls. In a HashMap.putIfAbsent the difference is even larger (5ms vs 1400ms). I don't understand why this function in URL has such a performance impact (only on windows openjdk). I think it might be a bug in openjdk.

Would it be possible to use an URI or a String as the key type in the cache of JarFileUrlKey? In my tests that would fix the performance issues with the jar classloader.

Comment From: philwebb

@bgoorden Thanks for re-raising the issue and for the further analysis. I'm quite confused as to why your getHostAddressSynchronized(u) method would be so much faster given that it's doing the same thing as java.net.URL.getHostAddress(). It's very odd and also annoying that the issue is so hard to reproduce. There must be something unique to the JVM or the setup of the machine.

Regardless, I think we can simply always return null for nested:// URLs so I've pushed a fix to try that. Since I'm not able to reproduce the problem locally, could you give the SNAPSHOT a go when the next 3.4.x and 3.5.x builds have finished and let me know the results?

Comment From: philwebb

Would it be possible to use an URI or a String as the key type in the cache of JarFileUrlKey? In my tests that would fix the performance issues with the jar classloader.

I intentionally wanted to use the URL directly to save the creation of Strings or URI objects (which are not cached). We could consider dropping the cache entirely if commit 2128a84492 doesn't work.

Comment From: bgoorden

@bgoorden Thanks for re-raising the issue and for the further analysis. I'm quite confused as to why your getHostAddressSynchronized(u) method would be so much faster given that it's doing the same thing as java.net.URL.getHostAddress(). It's very odd and also annoying that the issue is so hard to reproduce. There must be something unique to the JVM or the setup of the machine.

Regardless, I think we can simply always return null for nested:// URLs so I've pushed a fix to try that. Since I'm not able to reproduce the problem locally, could you give the SNAPSHOT a go when the next 3.4.x and 3.5.x builds have finished and let me know the results?

I will try to test the fix today!

Comment From: bgoorden

@philwebb I have tested with the 3.5 snapshot. The performance issues seem to have been solved with the new fix. I have tested it on both windows 11 and windows server and the performance is now similar to the classic loader. Thank you!

Comment From: philwebb

Thanks for testing it @bgoorden!