8150641: Repeated compilation with a long classpath significantly slower on JDK 9

Caching resolved zip paths, and their non-existence; introducing an abstraction over jrtfs, directory and zipfs.

Reviewed-by: jjg
This commit is contained in:
Jan Lahoda 2016-04-13 09:50:48 +02:00
parent 1889a6c4fa
commit 2471896266
2 changed files with 290 additions and 192 deletions

View File

@ -45,6 +45,7 @@ import com.sun.tools.javac.file.JavacFileManager;
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 @@ public final class JavacTool implements JavaCompiler {
? 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);
}

View File

@ -42,6 +42,7 @@ import java.nio.file.InvalidPathException;
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
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;
public DirectoryContainer(Path directory) {
this.directory = directory;
}
if (!Files.exists(d)) {
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 (!caseMapCheck(d, subdirectory)) {
return;
}
if (!Files.exists(d)) {
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;
}
if (!caseMapCheck(d, subdirectory)) {
return;
}
if (realDirectory == null)
realDirectory = fsInfo.getCanonicalFile(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;
}
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);
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);
}
}
}
}
@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;
}
@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<>();
public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException, SecurityException {
this.archivePath = archivePath;
this.fileSystem = FileSystems.newFileSystem(archivePath, null);
}
Path containerSubdir = subdirectory.resolveAgainst(fs);
if (!Files.exists(containerSubdir)) {
return;
}
/**
* 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);
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;
}
}
if (resolvedSubdirectory == null)
return ;
boolean isValid(Path fileName) {
if (fileName == null) {
return true;
} else {
String name = fileName.toString();
if (name.endsWith("/")) {
name = name.substring(0, name.length() - 1);
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;
}
return SourceVersion.isIdentifier(name);
}
}
@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);
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);
}
}
return FileVisitResult.CONTINUE;
}
});
@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;
}
});
}
@Override
public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
Path p = resolvePath(name);
if (p != null)
return PathFileObject.forJarPath(JavacFileManager.this, p, userPath);
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
}
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
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 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil
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;