8342040: Further improve entry lookup performance for multi-release JARs

Co-authored-by: Claes Redestad <redestad@openjdk.org>
Reviewed-by: redestad
This commit is contained in:
Eirik Bjørsnøs 2024-10-28 18:21:18 +00:00
parent d2e716eb72
commit d49f21043b
4 changed files with 64 additions and 63 deletions

View file

@ -40,6 +40,7 @@ import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
@ -597,26 +598,16 @@ public class JarFile extends ZipFile {
}
private JarEntry getVersionedEntry(String name, JarEntry defaultEntry) {
if (!name.startsWith(META_INF)) {
int[] versions = JUZFA.getMetaInfVersions(this);
if (BASE_VERSION_FEATURE < versionFeature && versions.length > 0) {
// search for versioned entry
for (int i = versions.length - 1; i >= 0; i--) {
int version = versions[i];
// skip versions above versionFeature
if (version > versionFeature) {
continue;
}
// skip versions below base version
if (version < BASE_VERSION_FEATURE) {
break;
}
JarFileEntry vje = (JarFileEntry)super.getEntry(
META_INF_VERSIONS + version + "/" + name);
if (vje != null) {
return vje.withBasename(name);
}
if (BASE_VERSION_FEATURE < versionFeature && !name.startsWith(META_INF)) {
BitSet versions = JUZFA.getMetaInfVersions(this, name);
int version = versions.previousSetBit(versionFeature);
while (version >= BASE_VERSION_FEATURE) {
JarFileEntry vje = (JarFileEntry)super.getEntry(
META_INF_VERSIONS + version + "/" + name);
if (vje != null) {
return vje.withBasename(name);
}
version = versions.previousSetBit(version - 1);
}
}
return defaultEntry;

View file

@ -37,23 +37,7 @@ import java.nio.charset.Charset;
import java.nio.file.InvalidPathException;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.Files;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.jar.JarEntry;
@ -64,6 +48,7 @@ import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.access.JavaUtilJarAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.DecimalDigits;
import jdk.internal.util.OperatingSystem;
import jdk.internal.perf.PerfCounter;
import jdk.internal.ref.CleanerFactory;
@ -1090,19 +1075,23 @@ public class ZipFile implements ZipConstants, Closeable {
}
/**
* Returns the versions for which there exists a non-directory
* entry that begin with "META-INF/versions/" (case ignored).
* Returns a BitSet where the set bits represents versions found for
* the given entry name. For performance reasons, the name is looked
* up only by hashcode, meaning the result is an over-approximation.
* This method is used in JarFile, via SharedSecrets, as an
* optimization when looking up potentially versioned entries.
* Returns an empty array if no versioned entries exist.
* Returns an empty BitSet if no versioned entries exist for this
* name.
*/
private int[] getMetaInfVersions() {
private BitSet getMetaInfVersions(String name) {
synchronized (this) {
ensureOpen();
return res.zsrc.metaVersions;
return res.zsrc.metaVersions.getOrDefault(ZipCoder.hash(name), EMPTY_VERSIONS);
}
}
private static final BitSet EMPTY_VERSIONS = new BitSet();
/**
* Returns the value of the System property which indicates whether the
* Extra ZIP64 validation should be disabled.
@ -1139,8 +1128,8 @@ public class ZipFile implements ZipConstants, Closeable {
return ((ZipFile)jar).getManifestName(onlyIfHasSignatureRelatedFiles);
}
@Override
public int[] getMetaInfVersions(JarFile jar) {
return ((ZipFile)jar).getMetaInfVersions();
public BitSet getMetaInfVersions(JarFile jar, String name) {
return ((ZipFile)jar).getMetaInfVersions(name);
}
@Override
public Enumeration<JarEntry> entries(ZipFile zip) {
@ -1175,7 +1164,8 @@ public class ZipFile implements ZipConstants, Closeable {
private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
// "META-INF/".length()
private static final int META_INF_LEN = 9;
private static final int[] EMPTY_META_VERSIONS = new int[0];
// "META-INF/versions//".length()
private static final int META_INF_VERSIONS_LEN = 19;
// CEN size is limited to the maximum array size in the JDK
private static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
@ -1192,7 +1182,7 @@ public class ZipFile implements ZipConstants, Closeable {
private int manifestPos = -1; // position of the META-INF/MANIFEST.MF, if exists
private int manifestNum = 0; // number of META-INF/MANIFEST.MF, case insensitive
private int[] signatureMetaNames; // positions of signature related entries, if such exist
private int[] metaVersions; // list of unique versions found in META-INF/versions/
private Map<Integer, BitSet> metaVersions; // Versions found in META-INF/versions/, by entry name hash
private final boolean startsWithLoc; // true, if ZIP file starts with LOCSIG (usually true)
// A Hashmap for all entries.
@ -1574,7 +1564,7 @@ public class ZipFile implements ZipConstants, Closeable {
manifestPos = -1;
manifestNum = 0;
signatureMetaNames = null;
metaVersions = EMPTY_META_VERSIONS;
metaVersions = null;
}
private static final int BUF_SIZE = 8192;
@ -1759,8 +1749,6 @@ public class ZipFile implements ZipConstants, Closeable {
// list for all meta entries
ArrayList<Integer> signatureNames = null;
// Set of all version numbers seen in META-INF/versions/
Set<Integer> metaVersionsSet = null;
// Iterate through the entries in the central directory
int idx = 0; // Index into the entries array
@ -1799,9 +1787,19 @@ public class ZipFile implements ZipConstants, Closeable {
// performance in multi-release jar files
int version = getMetaVersion(entryPos + META_INF_LEN, nlen - META_INF_LEN);
if (version > 0) {
if (metaVersionsSet == null)
metaVersionsSet = new TreeSet<>();
metaVersionsSet.add(version);
try {
// Compute hash code of name from "META-INF/versions/{version)/{name}
int prefixLen = META_INF_VERSIONS_LEN + DecimalDigits.stringSize(version);
int hashCode = zipCoderForPos(pos).checkedHash(cen,
entryPos + prefixLen,
nlen - prefixLen);
// Register version for this hash code
if (metaVersions == null)
metaVersions = new HashMap<>();
metaVersions.computeIfAbsent(hashCode, _ -> new BitSet()).set(version);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
}
@ -1819,14 +1817,8 @@ public class ZipFile implements ZipConstants, Closeable {
signatureMetaNames[j] = signatureNames.get(j);
}
}
if (metaVersionsSet != null) {
metaVersions = new int[metaVersionsSet.size()];
int c = 0;
for (Integer version : metaVersionsSet) {
metaVersions[c++] = version;
}
} else {
metaVersions = EMPTY_META_VERSIONS;
if (metaVersions == null) {
metaVersions = Map.of();
}
if (pos != cen.length) {
zerror("invalid CEN header (bad header size)");