7

Recently I found one of my Maven project have 100+ jar dependencies. FWIK a zip archive doesn't have index at all, so it should scan the whole zip to determine if it contains a specific path.

But I found Java resolve class names against so many jars rather fast, why?

4
  • 2
    The jar mechanism supports indexes: download.oracle.com/javase/1.4.2/docs/guide/jar/…
    – user1249
    May 20, 2011 at 9:25
  • @Thorbjørn: It seems the INDEX.LIST file is rarely used.
    – Lenik
    May 20, 2011 at 14:21
  • no it doesn't (most likely because it is very rarely needed, and any duplication of information tend to go out of sync), but jar-archives DO support indexes.
    – user1249
    May 20, 2011 at 14:26
  • I've just made a stat over 3382 jars, 74 jars contains INDEX.LIST. But you are right, jar or zip archives do support indexes. So it's my mistake.
    – Lenik
    May 20, 2011 at 15:53

2 Answers 2

1

The ZIP format (which JAR is an extension of) consists of a set of compressed sections and an index section at the end. The index section contains the full filenames (well, full relative to the root of the ZIP) of the files contained within the ZIP, together with other metadata (e.g., where the compressed data is) which means that finding what is in a ZIP is actually a very fast operation. Since a class maps to a single .class file in a trivial way, finding whether a JAR contains it is itself very fast even before considering any caching.

This all stems from the ZIP format's original use as a multi-disk compressed archive format; when expanding, you'd have to unzip by inserting the last disk of a set (so that the index could be read) before starting to deal with the compressed data from the beginning of the first disk. Of course, if you ran out of disks before you finished writing the archive, you were completely SOL…

6

It doesn't explore every reference until it has to

According to the JVM specification the bootstrap loader is specified to works as follows:

5.3.1 Loading Using the Bootstrap Class Loader The following steps are used to load and thereby create the nonarray class or interface C denoted by N using the bootstrap class loader.

First, the Java virtual machine determines whether the bootstrap class loader has already been recorded as an initiating loader of a class or interface denoted by N. If so, this class or interface is C, and no class creation is necessary.

Otherwise, the Java virtual machine performs one of the following two operations in order to load C:

1: The Java virtual machine searches for a purported representation of C in a platform-dependent manner. Note that there is no guarantee that a purported representation found is valid or is a representation of C.

Typically, a class or interface will be represented using a file in a hierarchical file system. The name of the class or interface will usually be encoded in the pathname of the file.

This phase of loading must detect the following error:

  • If no purported representation of C is found, loading throws an instance of NoClassDefFoundError or an instance of one of its subclasses.

Then the Java virtual machine attempts to derive a class denoted by N using the bootstrap class loader from the purported representation using the algorithm found in Section 5.3.5. That class is C.

2: The bootstrap class loader can delegate the loading of C to some user-defined class loader L by passing N to an invocation of a loadClass method on L. The result of the invocation is C. The Java virtual machine then records that the bootstrap loader is an initiating loader of C (§5.3.4).

Note the use of platform-dependent manner. This means that when searching for a particular instance of a class the JVM is required to explore a file system of some kind. In the case of your application it is a bunch of JARs.

As it searches through the classpath the JVM makes it's own internal index (probably an efficient Map) based on the JAR name and the paths it has encountered during the dynamic linking process. This index grows as more JARs are explored to resolve all references, but may not include all JARs unless not exploring them would cause a ClassNotFoundException.

This loading process is helped by the fact that the JAR file specification provides a facility for /META-INF/INDEX.LIST which acts as a trusted index of class definitions within the JAR.

An interesting side-effect of this process is that duplicate class definitions with the same name and package but different method signatures won't necessarily be detected until execution takes place (NoSuchMethodException etc)

2
  • Very detail answer. I think the "lazy" feature doesn't help much. It's very possible that the first class to be resolved (i.e., the Main class) is contained in the last jar in the 100 jars, and thus, all jars are scanned until the Main class is found in the last jar.
    – Lenik
    May 20, 2011 at 16:01
  • @Lenik You may want to consider reducing the number of dependencies in your application by using modules instead - that should help in many ways.
    – Gary
    May 20, 2011 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.