-
Bug
-
Resolution: Fixed
-
P3
-
1.4.2, 5.0, 5.0u5, 5.0u6
-
b05
-
b01
-
x86, sparc
-
solaris_10, windows_xp
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-2132145 | 6 | Sean Mullan | P3 | Resolved | Fixed | b65 |
JDK-2132146 | 1.4.2_13 | Abhijit Saha | P3 | Resolved | Fixed | b01 |
java version "1.4.2_10"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_10-b03)
Java HotSpot(TM) Client VM (build 1.4.2_10-b03, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
When any of the following JRE releases:
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_10-b03)
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
The time it takes for Sun to verify a signed JAR file increases significantly. This issue specifically effects any Java products that contain a JCE/JCE cryptographic provider, which is signed (and must be signed). The performance reduction is directly related to the number of entries in the JAR file and the number of entries in the manifest. For example, below is the slow-down observed when accessing an AES cipher from a third-party JCE/JCA provider that is shipped in a signed JAR file of various sizes:
Case #1
------------
JAR file size: 800KB
# JAR entries: 400
Performance Impact: 2x slowdown (1.3s versus 2.8s)
Case #2
-------------
JAR file size: 3740KB
# JAR entries: 2465
Performance Impact: 20x slowdown (3.1s versus 56.7s)
The performance comparison was done using JRE 1.5.0_03(fast) and JRE 1.5.0_05 (extremely slow). The larger the signed JAR file, the worse the performance penalty. A start-up performance penalty of over 1 minute is simply not acceptable.
The problem results from a change that was made to the JAR classes; this change is also described in bug 6349606. It appears that a change was made in JarFile.getInputStream() such that a call to this method results in call to getManifest(). When using the JarFile class, no performance degradation is seen because JarFile.java has a manifest caching mechanism. However, when using sun.net.www.protocol.jar.URLJarFile, the getManifest() call performs a deep copy of the manifest and does not have a caching mechanism (or at least one that works). This means that when each entry of the JAR file is read, the entire Manifest is copied (which means copying every entry in the Manifest)... this has a disastrous impact on performance.
Sun's JAR verification mechanism uses URLJarFile, so this impacts the performance of JAR verification.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The following program is the simplest demonstration of the problem; it accepts one argument which is the path of any signed jar file. It then attempts to read every entry in the JAR file using JarFile (causing the JAR file to be verified), then does the same thing using URLJarFile.
import java.io.File;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class DemoSlowJarRead {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("Usage: DemoURLJarFileRead <path to signed JAR file> ");
File file = new File(args[0]);
JarFile jarFile = new JarFile(file);
System.out.println("Reading JAR file from File: \n " + file);
readJarFile(jarFile);
// Read JAR from URL
URL url = new URL("jar:file:/" + file.getAbsolutePath() + "!/");
JarURLConnection conn = (JarURLConnection) url.openConnection();
conn.setUseCaches(false);
jarFile = conn.getJarFile();
System.out.println("Reading JAR file from URL: \n " + url);
readJarFile(jarFile);
}
private static void readJarFile(JarFile jarFile) throws Exception {
long l = System.currentTimeMillis();
Vector entriesVec = new Vector();
// Ensure the jar file is signed.
Manifest man = jarFile.getManifest();
if (man == null) {
throw new SecurityException("The jar file is not signed");
}
byte[] buffer = new byte[8192];
int i = 0;
Enumeration entries = jarFile.entries();
while (entries.hasMoreElements()) {
i++;
JarEntry je = (JarEntry) entries.nextElement();
// Skip directories.
if (je.isDirectory())
continue;
entriesVec.addElement(je);
InputStream is = jarFile.getInputStream(je);
// Read in each jar entry. A security exception will
// be thrown if a signature/digest check fails.
while ((is.read(buffer, 0, buffer.length)) != -1) {
// Don't care
}
is.close();
}
l = System.currentTimeMillis() - l;
System.out.println("\nManifest Entries: " + man.getEntries().size());
System.out.println("JAR File Entries: " + i);
System.out.println("Took: " + l / 1000.0 + "s\n\n");
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected result is that both the JarFile and URLJarFile method of reading the JAR file should have near identical performance. The actual result is much different.
These results were produced using JRE 1.5.0_03
RESULT 1 (800KB JAR File)
------------------------------------------
Reading JAR file from File:
D:\JTK\7.1 SP1\patch\100821\etjava\lib\entbase.jar
Manifest Entries: 439
JAR File Entries: 490
Took: 0.532s
Reading JAR file from URL:
jar:file:/D:\JTK\7.1 SP1\patch\100821\etjava\lib\entbase.jar!/
Manifest Entries: 439
JAR File Entries: 490
Took: 0.328s
RESULT 2 (3740KB JAR File)
------------------------------------------
Reading JAR file from File:
D:\JTK\7.1 SP1\patch\100821\etjava\lib\enttoolkit.jar
Manifest Entries: 2465
JAR File Entries: 2658
Took: 1.578s
Reading JAR file from URL:
jar:file:/D:\JTK\7.1 SP1\patch\100821\etjava\lib\enttoolkit.jar!/
Manifest Entries: 2465
JAR File Entries: 2658
Took: 1.047s
ACTUAL -
These results were produced using JRE 1.5.0_05
RESULT 1 (800KB JAR File)
------------------------------------------
Reading JAR file from File:
D:\JTK\7.1 SP1\patch\100821\etjava\lib\entbase.jar
Manifest Entries: 439
JAR File Entries: 490
Took: 0.5s
Reading JAR file from URL:
jar:file:/D:\JTK\7.1 SP1\patch\100821\etjava\lib\entbase.jar!/
Manifest Entries: 439
JAR File Entries: 490
Took: 0.843s
RESULT 2 (3740KB JAR File)
------------------------------------------
Reading JAR file from File:
D:\JTK\7.1 SP1\patch\100821\etjava\lib\enttoolkit.jar
Manifest Entries: 2465
JAR File Entries: 2658
Took: 1.547s
Reading JAR file from URL:
jar:file:/D:\JTK\7.1 SP1\patch\100821\etjava\lib\enttoolkit.jar!/
Manifest Entries: 2465
JAR File Entries: 2658
Took: 32.703s
REPRODUCIBILITY :
This bug can be reproduced always.
- backported by
-
JDK-2132145 Verification of signed JAR files is very slow (performance reduction)
-
- Resolved
-
-
JDK-2132146 Verification of signed JAR files is very slow (performance reduction)
-
- Resolved
-
- duplicates
-
JDK-6382773 Cipher.init() causes extremely long pauses and CPU pegged at 100% level
-
- Closed
-
-
JDK-6349606 REGRESSION: Bad Performance regression running XMLMark on 5.0_05 versus 5.0_03
-
- Closed
-
- relates to
-
JDK-6226269 JAR verification causes significant footprint increases
-
- Resolved
-
-
JDK-6423370 Fix for bug 5098318 prevents caching of JAR files containing cipher code
-
- Resolved
-