Hi,

while looking into java.util.jar.JarInputStream I've found a misuse of 
BufferedInputStream in checkManifest() method
where InputStream is wrapped into BIS and later read from with separately 
created byte[] buffer.

At runtime this means the data is copied from original IS into BIS and from 
there to byte[] resulting in redundant memory
allocation. The benchmark [1] demonstrates that for both scenarios (with and 
without verification) we can save memory
and reduce execution time by dropping BIS:

original

                                      Mode  Cnt       Score     Error   Units
read                                  avgt   50     230.301 ±   2.130   us/op
read:·gc.alloc.rate.norm              avgt   50  148929.020 ±  22.383    B/op
readNoVerify                          avgt   50     228.673 ±   0.556   us/op
readNoVerify:·gc.alloc.rate.norm      avgt   50  148133.555 ±   9.599    B/op

patched

                                      Mode  Cnt       Score     Error   Units
read                                  avgt   50     225.976 ±   0.543   us/op
read:·gc.alloc.rate.norm              avgt   50  140672.404 ±  20.732    B/op
readNoVerify                          avgt   50     229.563 ±   1.731   us/op
readNoVerify:·gc.alloc.rate.norm      avgt   50  139874.648 ±   7.054    B/op


Also InputStream.transferTo() can be called for the sake of code reuse.

Another snippets are located in MimeTable and in JmodFile:

- in MimeTable InputStream is passed into Properties.load() where they use
a buffer of exactly the same size (8192) as in BufferedInputStream. Benchmark 
[2]
demonstrates significant improvement when redundant bufferization is reduced:

                                               Mode  Cnt      Score     Error   
Units
buffered                                       avgt   50    155.477 ±   6.370   
us/op
buffered:·gc.alloc.rate.norm                   avgt   50  46263.355 ±   1.632   
 B/op
conventional                                   avgt   50    146.762 ±   3.481   
us/op
conventional:·gc.alloc.rate.norm               avgt   50  38014.521 ±   7.152   
 B/op

- in JmodFile we read only the 4 first bytes but BufferedInputStream fills in 
the whole 
buffer (by default again 8192)

Unfortunately, I couldn't construct benchmark for JmodFile, however I'm
sure there'll be improvement either.

Patch is attached. It also includes some tiny clean-ups in mentioned classes, 
e.g.
dead code removal

With best regards,
Sergey Tsypanov


1.

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class ReadManifestBenchmark {

  private final ClassLoader classLoader = getClass().getClassLoader();

  @Benchmark
  public Manifest read() throws IOException {
    return readManifest(true);
  }

  @Benchmark
  public Manifest readNoVerify() throws IOException {
    return readManifest(false);
  }

  private Manifest readManifest(boolean verify) throws IOException {
    try (
      InputStream resourceAsStream = 
classLoader.getResourceAsStream("jmh-core-1.23.jar");
      JarInputStream jarInputStream = new JarInputStream(resourceAsStream, 
verify)
    ) {
      return jarInputStream.getManifest();
    }
  }
}

2.

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class LoadPropertiesBenchmark {

  private final ClassLoader classLoader = getClass().getClassLoader();

  @Benchmark
  public Object conventional() throws IOException {
    try (InputStream is = 
getClass().getClassLoader().getResource("sun/net/www/content-types.properties").openStream())
 {
      Properties p = new Properties();
      p.load(is);
      return p;
    }
  }

  @Benchmark
  public Object buffered() throws IOException {
    try (InputStream is = new 
BufferedInputStream(getClass().getClassLoader().getResource("sun/net/www/content-types.properties").openStream()))
 {
      Properties p = new Properties();
      p.load(is);
      return p;
    }
  }
}
diff --git a/src/java.base/share/classes/java/util/jar/JarInputStream.java b/src/java.base/share/classes/java/util/jar/JarInputStream.java
--- a/src/java.base/share/classes/java/util/jar/JarInputStream.java
+++ b/src/java.base/share/classes/java/util/jar/JarInputStream.java
@@ -90,7 +90,7 @@
     {
         if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
             man = new Manifest();
-            byte bytes[] = getBytes(new BufferedInputStream(this));
+            byte[] bytes = getBytes(this);
             man.read(new ByteArrayInputStream(bytes));
             closeEntry();
             if (doVerify) {
@@ -105,12 +105,8 @@
     private byte[] getBytes(InputStream is)
         throws IOException
     {
-        byte[] buffer = new byte[8192];
         ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
-        int n;
-        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
-            baos.write(buffer, 0, n);
-        }
+        is.transferTo(baos);
         return baos.toByteArray();
     }
 
@@ -153,7 +149,7 @@
             // At this point, we might have parsed all the meta-inf
             // entries and have nothing to verify. If we have
             // nothing to verify, get rid of the JarVerifier object.
-            if (jv.nothingToVerify() == true) {
+            if (jv.nothingToVerify()) {
                 jv = null;
                 mev = null;
             } else {
diff --git a/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java b/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
--- a/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
+++ b/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
@@ -25,7 +25,6 @@
 
 package jdk.internal.jmod;
 
-import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -52,11 +51,10 @@
     };
 
     public static void checkMagic(Path file) throws IOException {
-        try (InputStream in = Files.newInputStream(file);
-             BufferedInputStream bis = new BufferedInputStream(in)) {
+        try (InputStream in = Files.newInputStream(file)) {
             // validate the header
             byte[] magic = new byte[4];
-            bis.read(magic);
+            in.read(magic);
             if (magic[0] != JMOD_MAGIC_NUMBER[0] ||
                 magic[1] != JMOD_MAGIC_NUMBER[1]) {
                 throw new IOException("Invalid JMOD file: " + file.toString());
@@ -72,7 +70,7 @@
     /**
      * JMOD sections
      */
-    public static enum Section {
+    public enum Section {
         CLASSES("classes"),
         CONFIG("conf"),
         HEADER_FILES("include"),
diff --git a/src/java.base/share/classes/sun/net/www/MimeTable.java b/src/java.base/share/classes/sun/net/www/MimeTable.java
--- a/src/java.base/share/classes/sun/net/www/MimeTable.java
+++ b/src/java.base/share/classes/sun/net/www/MimeTable.java
@@ -67,7 +67,6 @@
 
 
     private static final String filePreamble = "sun.net.www MIME content-types table";
-    private static final String fileMagic = "#" + filePreamble;
 
     MimeTable() {
         load();
@@ -242,7 +241,7 @@
                 throw new InternalError("default mime table not found");
         }
 
-        try (BufferedInputStream bin = new BufferedInputStream(in)) {
+        try (InputStream bin = in) {
             entries.load(bin);
         } catch (IOException e) {
             System.err.println("Warning: " + e.getMessage());
@@ -347,17 +346,6 @@
         // else illegal name exception
     }
 
-    String[] getExtensions(String list) {
-        StringTokenizer tokenizer = new StringTokenizer(list, ",");
-        int n = tokenizer.countTokens();
-        String[] extensions = new String[n];
-        for (int i = 0; i < n; i++) {
-            extensions[i] = tokenizer.nextToken();
-        }
-
-        return extensions;
-    }
-
     int getActionCode(String action) {
         for (int i = 0; i < MimeEntry.actionKeywords.length; i++) {
             if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) {

Reply via email to