JarFile.getVersionedEntry scalability with new release cadence

Eirik Bjørsnøs eirbjo at gmail.com
Sun Apr 12 08:50:20 UTC 2020


>
> I think you could tune away a significant part of that up front cost by
> using JUZFA.entryNameStream(this) instead of
> this.stream().map(ZipEntry::getName). This will avoid expanding each
> entry into a JarEntry internally. Perhaps this gets the up-front
> overhead down to more acceptable levels..?
>

I found JUZFA.getMetaInfEntryNames which made the up front scanning
cost evaporate.

Should be fast for other jars as well, at least when META-INF/ is sparsely
populated.

Here's an updated patch:

diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java
b/src/java.base/share/classes/java/util/jar/JarFile.java
index 1ec0f5bdae..95472604c0 100644
--- a/src/java.base/share/classes/java/util/jar/JarFile.java
+++ b/src/java.base/share/classes/java/util/jar/JarFile.java
@@ -161,7 +161,7 @@ public class JarFile extends ZipFile {
     private final Runtime.Version version;  // current version
     private final int versionFeature;       // version.feature()
     private boolean isMultiRelease;         // is jar multi-release?
-
+    private int[] versions;                 // which versions does the jar
contain
     // indicates if Class-Path attribute present
     private boolean hasClassPathAttribute;
     // true if manifest checked for special attributes
@@ -599,12 +599,13 @@ public class JarFile extends ZipFile {
     }

     private JarEntry getVersionedEntry(String name, JarEntry je) {
-        if (BASE_VERSION_FEATURE < versionFeature) {
+        int[] versions = this.versions;
+        if (BASE_VERSION_FEATURE < versionFeature && versions != null &&
versions.length > 0) {
             if (!name.startsWith(META_INF)) {
                 // search for versioned entry
-                int v = versionFeature;
-                while (v > BASE_VERSION_FEATURE) {
-                    JarFileEntry vje = getEntry0(META_INF_VERSIONS + v +
"/" + name);
+                int v = versions.length - 1;
+                while (v >= 0) {
+                    JarFileEntry vje = getEntry0(META_INF_VERSIONS +
versions[v] + "/" + name);
                     if (vje != null) {
                         return vje.withBasename(name);
                     }
@@ -1016,9 +1017,18 @@ public class JarFile extends ZipFile {
                         byte[] lbuf = new byte[512];
                         Attributes attr = new Attributes();
                         attr.read(new Manifest.FastInputStream(
-                            new ByteArrayInputStream(b)), lbuf);
-                        isMultiRelease = Boolean.parseBoolean(
-                            attr.getValue(Attributes.Name.MULTI_RELEASE));
+                                new ByteArrayInputStream(b)), lbuf);
+                        if(Boolean.parseBoolean(
+
 attr.getValue(Attributes.Name.MULTI_RELEASE))) {
+                            isMultiRelease = true;
+                            versions =
Stream.of(JUZFA.getMetaInfEntryNames(this))
+                                    .mapToInt(this::parseVersion)
+                                    .filter(v -> v != -1 && v >=
BASE_VERSION_FEATURE && v <= versionFeature)
+                                    .distinct()
+                                    .sorted()
+                                    .toArray();
+                        }
+
                     }
                 }
             }
@@ -1026,6 +1036,27 @@ public class JarFile extends ZipFile {
         }
     }

+    /**
+     * If {@code entryName} is a a versioned entry, parse and return the
version as an integer, otherwise return -1
+     */
+    private int parseVersion(String entryName) {
+        if(!entryName.startsWith(META_INF_VERSIONS)) {
+            return -1;
+        }
+
+        int separator = entryName.indexOf("/", META_INF_VERSIONS.length());
+
+        if(separator == -1) {
+            return -1;
+        }
+
+        try {
+            return Integer.parseInt(entryName, META_INF_VERSIONS.length(),
separator, 10);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
     synchronized void ensureInitialization() {
         try {
             maybeInstantiateVerifier();

Eirik.


More information about the core-libs-dev mailing list