Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6831731

URLClassLoader runs slower if given jar:file:/x.jar!/ than if given file:/x.jar

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 5.0
    • core-libs
    • x86
    • linux

      ---%<---
      import java.io.File;
      import java.lang.reflect.Field;
      import java.net.URL;
      import java.net.URLClassLoader;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      public class URLClassPathBug {
          public static void main(String[] args) throws Exception {
      // System.setSecurityManager(new SecurityManager() {
      // public @Override void checkPermission(Permission perm) {}
      // });
              File j = new File(System.getProperty("java.home"), "lib/rt.jar");
              assert j.isFile() : j;
              URL base = j.toURI().toURL();
              URL incorrect = base;
              URL correct = new URL("jar:" + base + "!/");
              for (URL u : new URL[] {incorrect, correct}) {
                  System.err.println("Checking: " + u);
                  List<Long> times = new ArrayList<Long>();
                  for (int i = 0; i < 20; i++) {
                      times.add(time(u));
                  }
                  Collections.sort(times);
                  System.err.printf(" median time: %5dusec\n", times.get(times.size() / 2));
                  debug(u);
              }
          }
          private static long time(URL u) throws Exception {
              ClassLoader l = new URLClassLoader(new URL[] {u});
              System.gc();
              long start = System.nanoTime();
              for (int i = 0; i < 10; i++) {
                  l.getResource("nonexistent");
              }
              long end = System.nanoTime();
              long usec = (end - start) / 1000;
              System.err.printf(" %5dusec\n", usec);
              return usec;
          }
          private static void debug(URL u) throws Exception {
              ClassLoader l = new URLClassLoader(new URL[] {u});
              l.getResource("nonexistent");
              Field f = URLClassLoader.class.getDeclaredField("ucp");
              f.setAccessible(true);
              Object ucp = f.get(l);
              f = ucp.getClass().getDeclaredField("loaders");
              f.setAccessible(true);
              System.err.println(" l.ucp.loaders=" + f.get(ucp));
          }
      }
      ---%<---

      JDK 6 produces in a typical run:

      ---%<---
      Checking: file:/space/jdk1.6.0_11/jre/lib/rt.jar
         7823¿sec
         3049¿sec
         2404¿sec
        17242¿sec
         1820¿sec
         2815¿sec
         6870¿sec
         1677¿sec
        12400¿sec
         1549¿sec
         1604¿sec
         1667¿sec
         1559¿sec
         1536¿sec
         1522¿sec
         1534¿sec
         1548¿sec
         1587¿sec
         1585¿sec
         1589¿sec
        median time: 1667¿sec
        l.ucp.loaders=[sun.misc.URLClassPath$JarLoader@1833955]
      Checking: jar:file:/space/jdk1.6.0_11/jre/lib/rt.jar!/
         5187¿sec
         3655¿sec
         3775¿sec
         3512¿sec
         3365¿sec
         3178¿sec
         3073¿sec
         3251¿sec
         3402¿sec
         2852¿sec
         2682¿sec
         2721¿sec
         2786¿sec
         2753¿sec
         2691¿sec
         2660¿sec
         2680¿sec
         2676¿sec
         2870¿sec
         2614¿sec
        median time: 2870¿sec
        l.ucp.loaders=[sun.misc.URLClassPath$Loader@b66cc]
      ---%<---

      If you treat 'jar'-protocol URLs consistently with all other URLs - which is to say, consider 'u' to be a classpath element if 'new URL(u, r)' is the correct way to refer to a classpath resource 'r' - then you should use 'jar:file:/x.jar!/' as a classpath entry representing the root directory entry of the file '/x.jar'. Indeed URLClassLoader accepts this usage in its constructor. (It always _returns_ the 'jar'-protocol URLs from calls to getResource.)

      Unfortunately, there is a bug in URLClassPath which causes the correct URLs to be searched more slowly than others. URLClassPath.getLoader(URL) does not even check for the 'jar' protocol, falling back to the generic Loader implementation rather than the specialized JarLoader implementation. Loader.findResource uses an inefficient search mechanism, based on actually opening a URLConnection and treating an IOException as "missing".

      URLCP.Loader also calls check(URL) whether the resource can be found or not, which in the presence of a SecurityManager forces creation of a FilePermission, which is relatively slow due to the need to canonicalize the file path.
      Apparently Bugster is not Unicode-compliant; the special char in the source was intended to be GREEK SMALL LETTER MU.

            Unassigned Unassigned
            jglick Jesse Glick (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: