8068686: Remove meta-index support

Reviewed-by: alanb, erikj, mchung
This commit is contained in:
Chris Hegarty 2016-02-17 11:45:46 +00:00
parent 7852a473da
commit 2753ab4610
6 changed files with 3 additions and 749 deletions

View File

@ -41,10 +41,6 @@ BUILD_TOOLS_JDK := $(call SetupJavaCompilationCompileTarget, \
TOOL_ADDJSUM = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.addjsum.AddJsum
# The buildmetaindex tool creates a meta-index to make core class loaders lazier.
TOOL_BUILDMETAINDEX = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.buildmetaindex.BuildMetaIndex
TOOL_COMPILEFONTCONFIG = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
build.tools.compilefontconfig.CompileFontConfig

View File

@ -1,398 +0,0 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package build.tools.buildmetaindex;
import java.io.*;
import java.util.*;
import java.util.jar.*;
/** Constructs a meta-index of the specified jar files. The meta-index
contains prefixes of packages contained in these jars, indexed by
the jar file name. It is intended to be consumed by the JVM to
allow the boot class loader to be made lazier. For example, when
class data sharing is enabled, the presence of the meta-index
allows the JVM to skip opening rt.jar if all of the dependent
classes of the application are in the shared archive. A similar
mechanism could be useful at the application level as well, for
example to make the extension class loader lazier.
<p> The contents of the meta-index file for jre/lib look something
like this:
<PRE>
% VERSION 2
# charsets.jar
sun/
# jce.jar
javax/
! jsse.jar
sun/
com/sun/net/
javax/
com/sun/security/
! rt.jar
org/w3c/
com/sun/image/
com/sun/org/
com/sun/imageio/
com/sun/accessibility/
javax/
...
</PRE>
<p> It is a current invariant of the code in the JVM which
consumes the meta-index that the meta-index indexes only jars in
one directory. It is acceptable for jars in that directory to not
be mentioned in the meta-index. The meta-index is designed more to
be able to perform a quick rejection test of the presence of a
particular class in a particular jar file than to be a precise
index of the contents of the jar. */
public class BuildMetaIndex {
public static void main(String[] args) throws IOException {
/* The correct usage of this class is as following:
* java BuildMetaIndex -o <meta-index> <a list of jar files>
* So the argument length should be at least 3 and the first argument should
* be '-o'.
*/
if (args.length < 3 ||
!args[0].equals("-o")) {
printUsage();
System.exit(1);
}
try {
PrintStream out = new PrintStream(new FileOutputStream(args[1]));
out.println("% VERSION 2");
out.println("% WARNING: this file is auto-generated; do not edit");
out.println("% UNSUPPORTED: this file and its format may change and/or");
out.println("% may be removed in a future release");
for (int i = 2; i < args.length; i++) {
String filename = args[i];
JarMetaIndex jmi = new JarMetaIndex(filename);
HashSet<String> index = jmi.getMetaIndex();
if (index == null) {
continue;
}
/*
* meta-index file plays different role in JVM and JDK side.
* On the JVM side, meta-index file is used to speed up locating the
* class files only while on the JDK side, meta-index file is used to speed
* up the resources file and class file.
* To help the JVM and JDK code to better utilize the information in meta-index
* file, we mark the jar file differently. Here is the current rule we use (See
* JarFileKind.getMarkChar() method. )
* For jar file containing only class file, we put '!' before the jar file name;
* for jar file containing only resources file, we put '@' before the jar file name;
* for jar file containing both resources and class file, we put '#' before the jar name.
* Notice the fact that every jar file contains at least the manifest file, so when
* we say "jar file containing only class file", we don't include that file.
*/
out.println(jmi.getJarFileKind().getMarkerChar() + " " + filename);
for (String entry : index) {
out.println(entry);
}
}
out.flush();
out.close();
} catch (FileNotFoundException fnfe) {
System.err.println("FileNotFoundException occurred");
System.exit(2);
}
}
private static void printUsage() {
String usage =
"BuildMetaIndex is used to generate a meta index file for the jar files\n" +
"you specified. The following is its usage:\n" +
" java BuildMetaIndex -o <the output meta index file> <a list of jar files> \n" +
" You can specify *.jar to refer to all the jar files in the current directory";
System.err.println(usage);
}
}
enum JarFileKind {
CLASSONLY ('!'),
RESOURCEONLY ('@'),
MIXED ('#');
private char markerChar;
JarFileKind(char markerChar) {
this.markerChar = markerChar;
}
public char getMarkerChar() {
return markerChar;
}
}
/*
* JarMetaIndex associates the jar file with a set of what so called
* "meta-index" of the jar file. Essentially, the meta-index is a list
* of class prefixes and the plain files contained in META-INF directory (
* not include the manifest file itself). This will help sun.misc.URLClassPath
* to quickly locate the resource file and hotspot VM to locate the class file.
*
*/
class JarMetaIndex {
private JarFile jar;
private volatile HashSet<String> indexSet;
/*
* A hashmap contains a mapping from the prefix string to
* a hashset which contains a set of the second level of prefix string.
*/
private HashMap<String, HashSet<String>> knownPrefixMap = new HashMap<>();
/**
* Special value for the HashSet to indicate that there are classes in
* the top-level package.
*/
private static final String TOP_LEVEL = "TOP";
/*
* A class for mapping package prefixes to the number of
* levels of package elements to include.
*/
static class ExtraLevel {
public ExtraLevel(String prefix, int levels) {
this.prefix = prefix;
this.levels = levels;
}
String prefix;
int levels;
}
/*
* A list of the special-cased package names.
*/
private static ArrayList<ExtraLevel> extraLevels = new ArrayList<>();
static {
// The order of these statements is significant,
// since we stop looking after the first match.
// Need more precise information to disambiguate
// (illegal) references from applications to
// obsolete backported collections classes in
// com/sun/java/util
extraLevels.add(new ExtraLevel("com/sun/java/util/", Integer.MAX_VALUE));
extraLevels.add(new ExtraLevel("com/sun/java/", 4));
// Need more information than just first two package
// name elements to determine that classes in
// deploy.jar are not in rt.jar
extraLevels.add(new ExtraLevel("com/sun/", 3));
// Need to make sure things in jfr.jar aren't
// confused with other com/oracle/** packages
extraLevels.add(new ExtraLevel("com/oracle/jrockit", 3));
}
/*
* We add maximum 5 second level entries to "sun", "jdk", "java" and
* "javax" entries. Tune this parameter to get a balance on the
* cold start and footprint.
*/
private static final int MAX_PKGS_WITH_KNOWN_PREFIX = 5;
private JarFileKind jarFileKind;
JarMetaIndex(String fileName) throws IOException {
jar = new JarFile(fileName);
knownPrefixMap.put("sun", new HashSet<String>());
knownPrefixMap.put("jdk", new HashSet<String>());
knownPrefixMap.put("java", new HashSet<String>());
knownPrefixMap.put("javax", new HashSet<String>());
}
/* Returns a HashSet contains the meta index string. */
HashSet<String> getMetaIndex() {
if (indexSet == null) {
synchronized(this) {
if (indexSet == null) {
indexSet = new HashSet<>();
Enumeration<JarEntry> entries = jar.entries();
boolean containsOnlyClass = true;
boolean containsOnlyResource = true;
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
/* We only look at the non-directory entry.
MANIFEST file is also skipped. */
if (entry.isDirectory()
|| name.equals("META-INF/MANIFEST.MF")) {
continue;
}
/* Once containsOnlyResource or containsOnlyClass
turns to false, no need to check the entry type.
*/
if (containsOnlyResource || containsOnlyClass) {
if (name.endsWith(".class")) {
containsOnlyResource = false;
} else {
containsOnlyClass = false;
}
}
/* Add the full-qualified name of plain files under
META-INF directory to the indexSet.
*/
if (name.startsWith("META-INF")) {
indexSet.add(name);
continue;
}
/* Add the prefix name to the knownPrefixMap if the
name starts with any string in the knownPrefix list.
*/
if (isPrefixKnown(name)) {
continue;
}
String[] pkgElements = name.split("/");
// Last one is the class name; definitely ignoring that
if (pkgElements.length > 2) {
String meta = "";
// Default is 2 levels of package elements
int levels = 2;
// But for some packages we add more elements
for(ExtraLevel el : extraLevels) {
if (name.startsWith(el.prefix)) {
levels = el.levels;
break;
}
}
for (int i = 0; i < levels && i < pkgElements.length - 1; i++) {
meta += pkgElements[i] + "/";
}
if (!meta.equals("")) {
indexSet.add(meta);
}
}
} // end of "while" loop;
// Add the second level package names to the indexSet for
// the predefined names such as "sun", "java" and "javax".
addKnownPrefix();
/* Set "jarFileKind" attribute. */
if (containsOnlyClass) {
jarFileKind = JarFileKind.CLASSONLY;
} else if (containsOnlyResource) {
jarFileKind = JarFileKind.RESOURCEONLY;
} else {
jarFileKind = JarFileKind.MIXED;
}
}
}
}
return indexSet;
}
/*
* Checks to see whether the name starts with a string which is in the predefined
* list. If it is among one of the predefined prefixes, add it to the knowPrefixMap
* and returns true, otherwise, returns false.
* Returns true if the name is in a predefined prefix list. Otherwise, returns false.
*/
boolean isPrefixKnown(String name) {
int firstSlashIndex = name.indexOf("/");
if (firstSlashIndex == -1) {
return false;
}
String firstPkgElement = name.substring(0, firstSlashIndex);
HashSet<String> pkgSet = knownPrefixMap.get(firstPkgElement);
/* The name does not starts with "sun", "java" or "javax". */
if (pkgSet == null) {
return false;
}
/* Add the second level package name to the corresponding hashset. */
int secondSlashIndex = name.indexOf("/", firstSlashIndex+1);
if (secondSlashIndex == -1) {
pkgSet.add(TOP_LEVEL);
} else {
String secondPkgElement = name.substring(firstSlashIndex+1, secondSlashIndex);
pkgSet.add(secondPkgElement);
}
return true;
}
/*
* Adds all the second level package elements for "sun", "java" and "javax"
* if the corresponding jar file does not contain more than
* MAX_PKGS_WITH_KNOWN_PREFIX such entries.
*/
void addKnownPrefix() {
if (indexSet == null) {
return;
}
/* Iterate through the hash map, add the second level package names
* to the indexSet if has any.
*/
for (String key : knownPrefixMap.keySet()) {
HashSet<String> pkgSetStartsWithKey = knownPrefixMap.get(key);
int setSize = pkgSetStartsWithKey.size();
if (setSize == 0) {
continue;
}
if (setSize > JarMetaIndex.MAX_PKGS_WITH_KNOWN_PREFIX ||
pkgSetStartsWithKey.contains(TOP_LEVEL)) {
indexSet.add(key + "/");
} else {
/* If the set contains less than MAX_PKGS_WITH_KNOWN_PREFIX, add
* them to the indexSet of the MetaIndex object.
*/
for (String secondPkgElement : pkgSetStartsWithKey) {
indexSet.add(key + "/" + secondPkgElement);
}
}
} // end the outer "for"
}
JarFileKind getJarFileKind() {
// Build meta index if it hasn't.
if (indexSet == null) {
indexSet = getMetaIndex();
}
return jarFileKind;
}
}

View File

@ -106,31 +106,11 @@ public class JarIndex {
/**
* Returns the jar index, or <code>null</code> if none.
*
* This single parameter version of the method is retained
* for binary compatibility with earlier releases.
*
* @param jar the JAR file to get the index from.
* @exception IOException if an I/O error has occurred.
*/
public static JarIndex getJarIndex(JarFile jar) throws IOException {
return getJarIndex(jar, null);
}
/**
* Returns the jar index, or <code>null</code> if none.
*
* @param jar the JAR file to get the index from.
* @exception IOException if an I/O error has occurred.
*/
public static JarIndex getJarIndex(JarFile jar, MetaIndex metaIndex) throws IOException {
JarIndex index = null;
/* If metaIndex is not null, check the meta index to see
if META-INF/INDEX.LIST is contained in jar file or not.
*/
if (metaIndex != null &&
!metaIndex.mayContain(INDEX_NAME)) {
return null;
}
JarEntry e = jar.getJarEntry(INDEX_NAME);
// if found, then load the index
if (e != null) {

View File

@ -319,21 +319,7 @@ public class Launcher {
if (bootClassPath == null)
return new URL[0];
// Skip empty path in boot class path i.e. not default to use CWD
File[] classPath = getClassPath(bootClassPath, false);
int len = classPath.length;
Set<File> seenDirs = new HashSet<File>();
for (int i = 0; i < len; i++) {
File curEntry = classPath[i];
// Negative test used to properly handle
// nonexistent jars on boot class path
if (!curEntry.isDirectory()) {
curEntry = curEntry.getParentFile();
}
if (curEntry != null && seenDirs.add(curEntry)) {
MetaIndex.registerDirectory(curEntry);
}
}
return pathToURLs(classPath);
return pathToURLs(getClassPath(bootClassPath, false));
}
}
);

View File

@ -1,274 +0,0 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.misc;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
* MetaIndex is intended to decrease startup time (in particular cold
* start, when files are not yet in the disk cache) by providing a
* quick reject mechanism for probes into jar files. The on-disk
* representation of the meta-index is a flat text file with per-jar
* entries indicating (generally speaking) prefixes of package names
* contained in the jar. As an example, here is an edited excerpt of
* the meta-index generated for jre/lib in the current build:
*
<PRE>
% VERSION 1
# charsets.jar
sun/
# jce.jar
javax/
! jsse.jar
sun/
com/sun/net/
javax/
com/sun/security/
@ resources.jar
com/sun/xml/
com/sun/rowset/
com/sun/org/
sun/
com/sun/imageio/
javax/
com/sun/java/swing/
META-INF/services/
com/sun/java/util/jar/pack/
com/sun/corba/
com/sun/jndi/
! rt.jar
org/w3c/
com/sun/imageio/
javax/
java/
sun/
...
</PRE>
* <p> A few notes about the design of the meta-index:
*
* <UL>
*
* <LI> It contains entries for multiple jar files. This is
* intentional, to reduce the number of disk accesses that need to be
* performed during startup.
*
* <LI> It is only intended to act as a fast reject mechanism to
* prevent application and other classes from forcing all jar files on
* the boot and extension class paths to be opened. It is not intended
* as a precise index of the contents of the jar.
*
* <LI> It should be as small as possible to reduce the amount of time
* required to parse it during startup. For example, adding on the
* secondary package element to java/ and javax/ packages
* ("javax/swing/", for example) causes the meta-index to grow
* significantly. This is why substrings of the packages have been
* chosen as the principal contents.
*
* <LI> It is versioned, and optional, to prevent strong dependencies
* between the JVM and JDK. It is also potentially applicable to more
* than just the boot and extension class paths.
*
* <LI> Precisely speaking, it plays different role in JVM and J2SE
* side. On the JVM side, meta-index file is used to speed up locating the
* class files only while on the J2SE side, meta-index file is used to speed
* up the resources file & class file.
* To help the JVM and J2SE code to better utilize the information in meta-index
* file, we mark the jar file differently. Here is the current rule we use.
* For jar file containing only class file, we put '!' before the jar file name;
* for jar file containing only resources file, we put '@' before the jar file name;
* for jar file containing both resources and class file, we put '#' before the
* jar name.
* Notice the fact that every jar file contains at least the manifest file, so when
* we say "jar file containing only class file", we don't include that file.
*
* </UL>
*
* <p> To avoid changing the behavior of the current application
* loader and other loaders, the current MetaIndex implementation in
* the JDK requires that the directory containing the meta-index be
* registered with the MetaIndex class before construction of the
* associated URLClassPath. This prevents the need for automatic
* searching for the meta-index in the URLClassPath code and potential
* changes in behavior for non-core ClassLoaders.
*
* This class depends on make/tools/MetaIndex/BuildMetaIndex.java and
* is used principally by sun.misc.URLClassPath.
*/
public class MetaIndex {
// Maps jar file names in registered directories to meta-indices
private static volatile Map<File, MetaIndex> jarMap;
// List of contents of this meta-index
private String[] contents;
// Indicate whether the coresponding jar file is a pure class jar file or not
private boolean isClassOnlyJar;
//----------------------------------------------------------------------
// Registration of directories (which can cause parsing of the
// meta-index file if it is present), and fetching of parsed
// meta-indices
// jarMap is not strictly thread-safe when the meta index mechanism
// is extended for user-provided jar files in future.
public static MetaIndex forJar(File jar) {
return getJarMap().get(jar);
}
// 'synchronized' is added to protect the jarMap from being modified
// by multiple threads.
public static synchronized void registerDirectory(File dir) {
// Note that this does not currently check to see whether the
// directory has previously been registered, since the meta-index
// in a particular directory creates multiple entries in the
// jarMap. If this mechanism is extended beyond the boot and
// extension class paths (for example, automatically searching for
// meta-index files in directories containing jars which have been
// explicitly opened) then this code should be generalized.
//
// This method must be called from a privileged context.
File indexFile = new File(dir, "meta-index");
if (indexFile.exists()) {
try {
BufferedReader reader = new BufferedReader(new FileReader(indexFile));
String line = null;
String curJarName = null;
boolean isCurJarContainClassOnly = false;
List<String> contents = new ArrayList<String>();
Map<File, MetaIndex> map = getJarMap();
/* Convert dir into canonical form. */
dir = dir.getCanonicalFile();
/* Note: The first line should contain the version of
* the meta-index file. We have to match the right version
* before trying to parse this file. */
line = reader.readLine();
if (line == null ||
!line.equals("% VERSION 2")) {
reader.close();
return;
}
while ((line = reader.readLine()) != null) {
switch (line.charAt(0)) {
case '!':
case '#':
case '@': {
// Store away current contents, if any
if ((curJarName != null) && (contents.size() > 0)) {
map.put(new File(dir, curJarName),
new MetaIndex(contents,
isCurJarContainClassOnly));
contents.clear();
}
// Fetch new current jar file name
curJarName = line.substring(2);
if (line.charAt(0) == '!') {
isCurJarContainClassOnly = true;
} else if (isCurJarContainClassOnly) {
isCurJarContainClassOnly = false;
}
break;
}
case '%':
break;
default: {
contents.add(line);
}
}
}
// Store away current contents, if any
if ((curJarName != null) && (contents.size() > 0)) {
map.put(new File(dir, curJarName),
new MetaIndex(contents, isCurJarContainClassOnly));
}
reader.close();
} catch (IOException e) {
// Silently fail for now (similar behavior to elsewhere in
// extension and core loaders)
}
}
}
//----------------------------------------------------------------------
// Public APIs
//
public boolean mayContain(String entry) {
// Ask non-class file from class only jar returns false
// This check is important to avoid some class only jar
// files such as rt.jar are opened for resource request.
if (isClassOnlyJar && !entry.endsWith(".class")){
return false;
}
String[] conts = contents;
for (int i = 0; i < conts.length; i++) {
if (entry.startsWith(conts[i])) {
return true;
}
}
return false;
}
//----------------------------------------------------------------------
// Implementation only below this point
// @IllegalArgumentException if entries is null.
private MetaIndex(List<String> entries, boolean isClassOnlyJar)
throws IllegalArgumentException {
if (entries == null) {
throw new IllegalArgumentException();
}
contents = entries.toArray(new String[0]);
this.isClassOnlyJar = isClassOnlyJar;
}
private static Map<File, MetaIndex> getJarMap() {
if (jarMap == null) {
synchronized (MetaIndex.class) {
if (jarMap == null) {
jarMap = new HashMap<File, MetaIndex>();
}
}
}
assert jarMap != null;
return jarMap;
}
}

View File

@ -620,7 +620,6 @@ public class URLClassPath {
private JarFile jar;
private URL csu;
private JarIndex index;
private MetaIndex metaIndex;
private URLStreamHandler handler;
private HashMap<String, Loader> lmap;
private boolean closed = false;
@ -640,32 +639,7 @@ public class URLClassPath {
handler = jarHandler;
lmap = loaderMap;
if (!isOptimizable(url)) {
ensureOpen();
} else {
String fileName = url.getFile();
if (fileName != null) {
fileName = ParseUtil.decode(fileName);
File f = new File(fileName);
metaIndex = MetaIndex.forJar(f);
// If the meta index is found but the file is not
// installed, set metaIndex to null. A typical
// senario is charsets.jar which won't be installed
// when the user is running in certain locale environment.
// The side effect of null metaIndex will cause
// ensureOpen get called so that IOException is thrown.
if (metaIndex != null && !f.exists()) {
metaIndex = null;
}
}
// metaIndex is null when either there is no such jar file
// entry recorded in meta-index file or such jar file is
// missing in JRE. See bug 6340399.
if (metaIndex == null) {
ensureOpen();
}
}
ensureOpen();
}
@Override
@ -699,7 +673,7 @@ public class URLClassPath {
}
jar = getJarFile(csu);
index = JarIndex.getJarIndex(jar, metaIndex);
index = JarIndex.getJarIndex(jar);
if (index != null) {
String[] jarfiles = index.getJarFiles();
// Add all the dependent URLs to the lmap so that loaders
@ -854,12 +828,6 @@ public class URLClassPath {
* Returns the JAR Resource for the specified name.
*/
Resource getResource(final String name, boolean check) {
if (metaIndex != null) {
if (!metaIndex.mayContain(name)) {
return null;
}
}
try {
ensureOpen();
} catch (IOException e) {
@ -1002,10 +970,6 @@ public class URLClassPath {
return null;
}
if (metaIndex != null) {
return null;
}
ensureOpen();
if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { // Only get manifest when necessary