8150641: Repeated compilation with a long classpath significantly slower on JDK 9
authorjlahoda
Wed, 13 Apr 2016 09:50:48 +0200
changeset 37390 bf1552d6bc16
parent 37389 9c137b83a8b8
child 37391 f2750c003cfb
8150641: Repeated compilation with a long classpath significantly slower on JDK 9 Summary: Caching resolved zip paths, and their non-existence; introducing an abstraction over jrtfs, directory and zipfs. Reviewed-by: jjg
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java	Tue Apr 12 22:23:11 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java	Wed Apr 13 09:50:48 2016 +0200
@@ -45,6 +45,7 @@
 import com.sun.tools.javac.main.Arguments;
 import com.sun.tools.javac.main.Option;
 import com.sun.tools.javac.file.BaseFileManager;
+import com.sun.tools.javac.file.CacheFSInfo;
 import com.sun.tools.javac.util.ClientCodeException;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.DefinedBy;
@@ -95,6 +96,7 @@
                 ? new PrintWriter(System.err, true)
                 : new PrintWriter(new OutputStreamWriter(System.err, charset), true);
         context.put(Log.outKey, pw);
+        CacheFSInfo.preRegister(context);
         return new JavacFileManager(context, true, charset);
     }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Tue Apr 12 22:23:11 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Wed Apr 13 09:50:48 2016 +0200
@@ -42,6 +42,7 @@
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.ProviderNotFoundException;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
@@ -266,30 +267,137 @@
         System.out.println(message);
     }
 
-    /**
-     * Insert all files in a subdirectory of the platform image
-     * which match fileKinds into resultList.
-     */
-    private void listJRTImage(RelativeDirectory subdirectory,
-                               Set<JavaFileObject.Kind> fileKinds,
-                               boolean recurse,
-                               ListBuffer<JavaFileObject> resultList) throws IOException {
-        JRTIndex.Entry e = getJRTIndex().getEntry(subdirectory);
-        if (symbolFileEnabled && e.ctSym.hidden)
-            return;
-        for (Path file: e.files.values()) {
-            if (fileKinds.contains(getKind(file))) {
-                JavaFileObject fe
-                        = PathFileObject.forJRTPath(JavacFileManager.this, file);
-                resultList.append(fe);
+    private final Map<Path, Container> containers = new HashMap<>();
+
+    synchronized Container getContainer(Path path) throws IOException {
+        Container fs = containers.get(path);
+
+        if (fs != null) {
+            return fs;
+        }
+
+        if (fsInfo.isFile(path) && path.equals(Locations.thisSystemModules)) {
+            containers.put(path, fs = new JRTImageContainer());
+            return fs;
+        }
+
+        Path realPath = fsInfo.getCanonicalFile(path);
+
+        fs = containers.get(realPath);
+
+        if (fs != null) {
+            containers.put(path, fs);
+            return fs;
+        }
+
+        BasicFileAttributes attr = null;
+
+        try {
+            attr = Files.readAttributes(realPath, BasicFileAttributes.class);
+        } catch (IOException ex) {
+            //non-existing
+            fs = MISSING_CONTAINER;
+        }
+
+        if (attr != null) {
+            if (attr.isDirectory()) {
+                fs = new DirectoryContainer(realPath);
+            } else {
+                try {
+                    fs = new ArchiveContainer(realPath);
+                } catch (ProviderNotFoundException | SecurityException ex) {
+                    throw new IOException(ex);
+                }
             }
         }
 
-        if (recurse) {
-            for (RelativeDirectory rd: e.subdirs) {
-                listJRTImage(rd, fileKinds, recurse, resultList);
+        containers.put(realPath, fs);
+        containers.put(path, fs);
+
+        return fs;
+    }
+
+    private interface Container {
+        /**
+         * Insert all files in subdirectory subdirectory of container which
+         * match fileKinds into resultList
+         */
+        public abstract void list(Path userPath,
+                                  RelativeDirectory subdirectory,
+                                  Set<JavaFileObject.Kind> fileKinds,
+                                  boolean recurse,
+                                  ListBuffer<JavaFileObject> resultList) throws IOException;
+        public abstract JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException;
+        public abstract void close() throws IOException;
+    }
+
+    private static final Container MISSING_CONTAINER =  new Container() {
+        @Override
+        public void list(Path userPath,
+                         RelativeDirectory subdirectory,
+                         Set<JavaFileObject.Kind> fileKinds,
+                         boolean recurse,
+                         ListBuffer<JavaFileObject> resultList) throws IOException {
+        }
+        @Override
+        public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
+            return null;
+        }
+        @Override
+        public void close() throws IOException {}
+    };
+
+    private final class JRTImageContainer implements Container {
+
+        /**
+         * Insert all files in a subdirectory of the platform image
+         * which match fileKinds into resultList.
+         */
+        @Override
+        public void list(Path userPath,
+                         RelativeDirectory subdirectory,
+                         Set<JavaFileObject.Kind> fileKinds,
+                         boolean recurse,
+                         ListBuffer<JavaFileObject> resultList) throws IOException {
+            try {
+                JRTIndex.Entry e = getJRTIndex().getEntry(subdirectory);
+                if (symbolFileEnabled && e.ctSym.hidden)
+                    return;
+                for (Path file: e.files.values()) {
+                    if (fileKinds.contains(getKind(file))) {
+                        JavaFileObject fe
+                                = PathFileObject.forJRTPath(JavacFileManager.this, file);
+                        resultList.append(fe);
+                    }
+                }
+
+                if (recurse) {
+                    for (RelativeDirectory rd: e.subdirs) {
+                        list(userPath, rd, fileKinds, recurse, resultList);
+                    }
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace(System.err);
+                log.error("error.reading.file", userPath, getMessage(ex));
             }
         }
+
+        @Override
+        public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
+            JRTIndex.Entry e = getJRTIndex().getEntry(name.dirname());
+            if (symbolFileEnabled && e.ctSym.hidden)
+                return null;
+            Path p = e.files.get(name.basename());
+            if (p != null) {
+                return PathFileObject.forJRTPath(JavacFileManager.this, p);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+        }
     }
 
     private synchronized JRTIndex getJRTIndex() {
@@ -300,164 +408,179 @@
 
     private JRTIndex jrtIndex;
 
+    private final class DirectoryContainer implements Container {
+        private final Path directory;
 
-    /**
-     * Insert all files in subdirectory subdirectory of directory directory
-     * which match fileKinds into resultList
-     */
-    private void listDirectory(Path directory, Path realDirectory,
-                               RelativeDirectory subdirectory,
-                               Set<JavaFileObject.Kind> fileKinds,
-                               boolean recurse,
-                               ListBuffer<JavaFileObject> resultList) {
-        Path d;
-        try {
-            d = subdirectory.resolveAgainst(directory);
-        } catch (InvalidPathException ignore) {
-            return;
-        }
-
-        if (!Files.exists(d)) {
-           return;
-        }
-
-        if (!caseMapCheck(d, subdirectory)) {
-            return;
+        public DirectoryContainer(Path directory) {
+            this.directory = directory;
         }
 
-        java.util.List<Path> files;
-        try (Stream<Path> s = Files.list(d)) {
-            files = (sortFiles == null ? s : s.sorted(sortFiles)).collect(Collectors.toList());
-        } catch (IOException ignore) {
-            return;
+        /**
+         * Insert all files in subdirectory subdirectory of directory userPath
+         * which match fileKinds into resultList
+         */
+        @Override
+        public void list(Path userPath,
+                         RelativeDirectory subdirectory,
+                         Set<JavaFileObject.Kind> fileKinds,
+                         boolean recurse,
+                         ListBuffer<JavaFileObject> resultList) throws IOException {
+            Path d;
+            try {
+                d = subdirectory.resolveAgainst(userPath);
+            } catch (InvalidPathException ignore) {
+                return ;
+            }
+
+            if (!Files.exists(d)) {
+               return;
+            }
+
+            if (!caseMapCheck(d, subdirectory)) {
+                return;
+            }
+
+            java.util.List<Path> files;
+            try (Stream<Path> s = Files.list(d)) {
+                files = (sortFiles == null ? s : s.sorted(sortFiles)).collect(Collectors.toList());
+            } catch (IOException ignore) {
+                return;
+            }
+
+            for (Path f: files) {
+                String fname = f.getFileName().toString();
+                if (fname.endsWith("/"))
+                    fname = fname.substring(0, fname.length() - 1);
+                if (Files.isDirectory(f)) {
+                    if (recurse && SourceVersion.isIdentifier(fname)) {
+                        list(userPath,
+                             new RelativeDirectory(subdirectory, fname),
+                             fileKinds,
+                             recurse,
+                             resultList);
+                    }
+                } else {
+                    if (isValidFile(fname, fileKinds)) {
+                        RelativeFile file = new RelativeFile(subdirectory, fname);
+                        JavaFileObject fe = PathFileObject.forDirectoryPath(JavacFileManager.this,
+                                file.resolveAgainst(directory), userPath, file);
+                        resultList.append(fe);
+                    }
+                }
+            }
         }
 
-        if (realDirectory == null)
-            realDirectory = fsInfo.getCanonicalFile(directory);
+        @Override
+        public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
+            try {
+                Path f = name.resolveAgainst(userPath);
+                if (Files.exists(f))
+                    return PathFileObject.forSimplePath(JavacFileManager.this,
+                            fsInfo.getCanonicalFile(f), f);
+            } catch (InvalidPathException ignore) {
+            }
+            return null;
+        }
 
-        for (Path f: files) {
-            String fname = f.getFileName().toString();
-            if (fname.endsWith("/"))
-                fname = fname.substring(0, fname.length() - 1);
-            if (Files.isDirectory(f)) {
-                if (recurse && SourceVersion.isIdentifier(fname)) {
-                    listDirectory(directory, realDirectory,
-                                  new RelativeDirectory(subdirectory, fname),
-                                  fileKinds,
-                                  recurse,
-                                  resultList);
-                }
-            } else {
-                if (isValidFile(fname, fileKinds)) {
-                    RelativeFile file = new RelativeFile(subdirectory, fname);
-                    JavaFileObject fe = PathFileObject.forDirectoryPath(this,
-                            file.resolveAgainst(realDirectory), directory, file);
-                    resultList.append(fe);
-                }
-            }
+        @Override
+        public void close() throws IOException {
         }
     }
 
-    /**
-     * Insert all files in subdirectory subdirectory of archive archivePath
-     * which match fileKinds into resultList
-     */
-    private void listArchive(Path archivePath,
-            RelativeDirectory subdirectory,
-            Set<JavaFileObject.Kind> fileKinds,
-            boolean recurse,
-            ListBuffer<JavaFileObject> resultList)
-                throws IOException {
-        FileSystem fs = getFileSystem(archivePath);
-        if (fs == null) {
-            return;
-        }
+    private final class ArchiveContainer implements Container {
+        private final Path archivePath;
+        private final FileSystem fileSystem;
+        private final Map<RelativePath, Path> pathCache = new HashMap<>();
 
-        Path containerSubdir = subdirectory.resolveAgainst(fs);
-        if (!Files.exists(containerSubdir)) {
-            return;
+        public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException, SecurityException {
+            this.archivePath = archivePath;
+            this.fileSystem = FileSystems.newFileSystem(archivePath, null);
         }
 
-        int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
-        Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);
-        Files.walkFileTree(containerSubdir, opts, maxDepth,
-                new SimpleFileVisitor<Path>() {
-                    @Override
-                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
-                        if (isValid(dir.getFileName())) {
-                            return FileVisitResult.CONTINUE;
-                        } else {
-                            return FileVisitResult.SKIP_SUBTREE;
+        /**
+         * Insert all files in subdirectory subdirectory of this archive
+         * which match fileKinds into resultList
+         */
+        @Override
+        public void list(Path userPath,
+                         RelativeDirectory subdirectory,
+                         Set<JavaFileObject.Kind> fileKinds,
+                         boolean recurse,
+                         ListBuffer<JavaFileObject> resultList) throws IOException {
+            Path resolvedSubdirectory = resolvePath(subdirectory);
+
+            if (resolvedSubdirectory == null)
+                return ;
+
+            int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
+            Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);
+            Files.walkFileTree(resolvedSubdirectory, opts, maxDepth,
+                    new SimpleFileVisitor<Path>() {
+                        @Override
+                        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
+                            if (isValid(dir.getFileName())) {
+                                return FileVisitResult.CONTINUE;
+                            } else {
+                                return FileVisitResult.SKIP_SUBTREE;
+                            }
                         }
-                    }
+
+                        boolean isValid(Path fileName) {
+                            if (fileName == null) {
+                                return true;
+                            } else {
+                                String name = fileName.toString();
+                                if (name.endsWith("/")) {
+                                    name = name.substring(0, name.length() - 1);
+                                }
+                                return SourceVersion.isIdentifier(name);
+                            }
+                        }
 
-                    boolean isValid(Path fileName) {
-                        if (fileName == null) {
-                            return true;
-                        } else {
-                            String name = fileName.toString();
-                            if (name.endsWith("/")) {
-                                name = name.substring(0, name.length() - 1);
+                        @Override
+                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                            if (attrs.isRegularFile() && fileKinds.contains(getKind(file.getFileName().toString()))) {
+                                JavaFileObject fe = PathFileObject.forJarPath(
+                                        JavacFileManager.this, file, archivePath);
+                                resultList.append(fe);
                             }
-                            return SourceVersion.isIdentifier(name);
+                            return FileVisitResult.CONTINUE;
                         }
-                    }
+                    });
+
+        }
+
+        @Override
+        public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
+            Path p = resolvePath(name);
+            if (p != null)
+                return PathFileObject.forJarPath(JavacFileManager.this, p, userPath);
 
-                    @Override
-                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
-                        if (attrs.isRegularFile() && fileKinds.contains(getKind(file.getFileName().toString()))) {
-                            JavaFileObject fe = PathFileObject.forJarPath(
-                                    JavacFileManager.this, file, archivePath);
-                            resultList.append(fe);
-                        }
-                        return FileVisitResult.CONTINUE;
-                    }
-                });
+            return null;
+        }
+
+        private synchronized Path resolvePath(RelativePath path) {
+            if (!pathCache.containsKey(path)) {
+                Path relativePath = path.resolveAgainst(fileSystem);
+
+                if (!Files.exists(relativePath)) {
+                    relativePath = null;
+                }
 
+                pathCache.put(path, relativePath);
+                return relativePath;
+            }
+            return pathCache.get(path);
+        }
+
+        @Override
+        public void close() throws IOException {
+            fileSystem.close();
+        }
     }
-
     /**
      * container is a directory, a zip file, or a non-existant path.
-     * Insert all files in subdirectory subdirectory of container which
-     * match fileKinds into resultList
      */
-    private void listContainer(Path container,
-                               RelativeDirectory subdirectory,
-                               Set<JavaFileObject.Kind> fileKinds,
-                               boolean recurse,
-                               ListBuffer<JavaFileObject> resultList)
-            throws IOException {
-        if (Files.isRegularFile(container) && container.equals(Locations.thisSystemModules)) {
-            try {
-                listJRTImage(subdirectory,
-                        fileKinds,
-                        recurse,
-                        resultList);
-            } catch (IOException ex) {
-                ex.printStackTrace(System.err);
-                log.error("error.reading.file", container, getMessage(ex));
-            }
-            return;
-        }
-
-        if  (Files.isDirectory(container)) {
-            listDirectory(container, null,
-                          subdirectory,
-                          fileKinds,
-                          recurse,
-                          resultList);
-            return;
-        }
-
-        if (Files.isRegularFile(container)) {
-            listArchive(container,
-                    subdirectory,
-                    fileKinds,
-                    recurse,
-                    resultList);
-        }
-    }
-
     private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
         JavaFileObject.Kind kind = getKind(s);
         return fileKinds.contains(kind);
@@ -498,18 +621,6 @@
         return j < 0;
     }
 
-    private FileSystem getFileSystem(Path path) throws IOException {
-        Path realPath = fsInfo.getCanonicalFile(path);
-        FileSystem fs = fileSystems.get(realPath);
-        if (fs == null) {
-            fileSystems.put(realPath, fs = FileSystems.newFileSystem(realPath, null));
-        }
-        return fs;
-    }
-
-    private final Map<Path,FileSystem> fileSystems = new HashMap<>();
-
-
     /** Flush any output resources.
      */
     @Override @DefinedBy(Api.COMPILER)
@@ -528,10 +639,10 @@
         }
 
         locations.close();
-        for (FileSystem fs: fileSystems.values()) {
-            fs.close();
+        for (Container container: containers.values()) {
+            container.close();
         }
-        fileSystems.clear();
+        containers.clear();
         contentCache.clear();
     }
 
@@ -570,8 +681,12 @@
         RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName);
         ListBuffer<JavaFileObject> results = new ListBuffer<>();
 
-        for (Path directory : path)
-            listContainer(directory, subdirectory, kinds, recurse, results);
+        for (Path directory : path) {
+            Container container = getContainer(directory);
+
+            container.list(directory, subdirectory, kinds, recurse, results);
+        }
+
         return results.toList();
     }
 
@@ -644,29 +759,10 @@
             return null;
 
         for (Path file: path) {
-            if (file.equals(Locations.thisSystemModules)) {
-                JRTIndex.Entry e = getJRTIndex().getEntry(name.dirname());
-                if (symbolFileEnabled && e.ctSym.hidden)
-                    continue;
-                Path p = e.files.get(name.basename());
-                if (p != null)
-                    return PathFileObject.forJRTPath(this, p);
-            } else if (Files.isDirectory(file)) {
-                try {
-                    Path f = name.resolveAgainst(file);
-                    if (Files.exists(f))
-                        return PathFileObject.forSimplePath(this,
-                                fsInfo.getCanonicalFile(f), f);
-                } catch (InvalidPathException ignore) {
-                }
-            } else if (Files.isRegularFile(file)) {
-                FileSystem fs = getFileSystem(file);
-                if (fs != null) {
-                    Path fsRoot = fs.getRootDirectories().iterator().next();
-                    Path f = name.resolveAgainst(fsRoot);
-                    if (Files.exists(f))
-                        return PathFileObject.forJarPath(this, f, file);
-                }
+            JavaFileObject fo = getContainer(file).getFileObject(file, name);
+
+            if (fo != null) {
+                return fo;
             }
         }
         return null;