Duplicate :
|
|
Duplicate :
|
|
Relates :
|
|
Relates :
|
FULL PRODUCT VERSION : 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.
|