8000316: Huge performance bottleneck in com.sun.tools.javac.comp.Check.localClassName
Summary: Speed up Check.localClassName by avoiding generating names known to be in use already
Reviewed-by: mcimadamore, jlahoda, sadayapalam
Contributed-by: dmitry.chuyko@oracle.com
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Oct 12 19:43:44 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Tue Oct 13 09:48:03 2015 +0530
@@ -409,6 +409,9 @@
* Class name generation
**************************************************************************/
+
+ private Map<Pair<Name, Name>, Integer> localClassNameIndexes = new HashMap<>();
+
/** Return name of local class.
* This is of the form {@code <enclClass> $ n <classname> }
* where
@@ -416,17 +419,28 @@
* classname is the simple name of the local class
*/
Name localClassName(ClassSymbol c) {
- for (int i=1; ; i++) {
- Name flatname = names.
- fromString("" + c.owner.enclClass().flatname +
- syntheticNameChar + i +
- c.name);
- if (compiled.get(flatname) == null) return flatname;
+ Name enclFlatname = c.owner.enclClass().flatname;
+ String enclFlatnameStr = enclFlatname.toString();
+ Pair<Name, Name> key = new Pair<>(enclFlatname, c.name);
+ Integer index = localClassNameIndexes.get(key);
+ for (int i = (index == null) ? 1 : index; ; i++) {
+ Name flatname = names.fromString(enclFlatnameStr
+ + syntheticNameChar + i + c.name);
+ if (compiled.get(flatname) == null) {
+ localClassNameIndexes.put(key, i + 1);
+ return flatname;
+ }
}
}
+ void clearLocalClassNameIndexes(ClassSymbol c) {
+ localClassNameIndexes.remove(new Pair<>(
+ c.owner.enclClass().flatname, c.name));
+ }
+
public void newRound() {
compiled.clear();
+ localClassNameIndexes.clear();
}
/* *************************************************************************
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Mon Oct 12 19:43:44 2015 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java Tue Oct 13 09:48:03 2015 +0530
@@ -472,6 +472,7 @@
if (csym == null) return;
typeEnvs.remove(csym);
chk.compiled.remove(csym.flatname);
+ chk.clearLocalClassNameIndexes(csym);
syms.classes.remove(csym.flatname);
super.visitClassDef(tree);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T8000316/T8000316.java Tue Oct 13 09:48:03 2015 +0530
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8000316
+ * @summary Huge performance bottleneck in com.sun.tools.javac.comp.Check.localClassName
+ * @modules jdk.compiler
+ * @run main T8000316
+ */
+
+import com.sun.source.util.JavacTask;
+import java.net.URI;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class T8000316 {
+
+ final static int N_METHODS = 1024;
+ final static int N_CLASSES = 8;
+
+ static class TestClass extends SimpleJavaFileObject {
+
+ String methTemplate = " public static Runnable get#N() {\n" +
+ " return new Runnable() {\n" +
+ " @Override\n" +
+ " public void run() {\n" +
+ " return;\n" +
+ " }\n" +
+ " };\n" +
+ " }\n";
+
+ String classTemplate = "\n\nclass Chain#N {\n\n#M }";
+
+ String source;
+
+ public TestClass() {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ StringBuilder buf = new StringBuilder();
+ StringBuilder fileBuf = new StringBuilder();
+
+ for (int i = 0 ; i < N_CLASSES ; i++) {
+ for (int j = 0; j < N_METHODS; j++) {
+ buf.append(methTemplate.replace("#N", String.valueOf(j)));
+ buf.append("\n");
+ }
+ fileBuf.append(classTemplate.replace("#M", buf.toString()).replace("#N", String.valueOf(i)));
+ buf = new StringBuilder();
+ source = fileBuf.toString();
+ }
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ ArrayList<JavaFileObject> sources = new ArrayList<>();
+ sources.add(new TestClass());
+ new T8000316().run(sources);
+ }
+
+ void run(List<JavaFileObject> sources) throws Exception {
+ javax.tools.DiagnosticListener<JavaFileObject> dc = (diagnostic) -> {
+ if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+ throw new AssertionError("unexpected diagnostic: " + diagnostic.getMessage(Locale.getDefault()));
+ }
+ };
+ JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+ try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
+ JavacTask ct = (JavacTask)comp.getTask(null, fm, dc,
+ null, null, sources);
+ ct.analyze();
+ }
+ }
+}