# HG changeset patch # User coleenp # Date 1273000328 14400 # Node ID 77261afdc5f2215c0e13703589e10e5e7d7417af # Parent 96d554193f72ccefb354f06fa43b69cce9aa851a 6935118: UseCompressedOops modification in methodOopDesc::sort_methods() causes JCK timeout Summary: Add comparison functions for compressed oops to use bubblesort. Reviewed-by: never, coleenp Contributed-by: volker.simonis@gmail.com diff -r 96d554193f72 -r 77261afdc5f2 src/share/vm/oops/methodOop.cpp --- a/src/share/vm/oops/methodOop.cpp Mon Apr 19 18:58:31 2010 -0400 +++ b/src/share/vm/oops/methodOop.cpp Tue May 04 15:12:08 2010 -0400 @@ -1114,6 +1114,20 @@ return ( a < b ? -1 : (a == b ? 0 : 1)); } + // We implement special compare versions for narrow oops to avoid + // testing for UseCompressedOops on every comparison. + static int method_compare_narrow(narrowOop* a, narrowOop* b) { + methodOop m = (methodOop)oopDesc::load_decode_heap_oop(a); + methodOop n = (methodOop)oopDesc::load_decode_heap_oop(b); + return m->name()->fast_compare(n->name()); + } + + static int method_compare_narrow_idempotent(narrowOop* a, narrowOop* b) { + int i = method_compare_narrow(a, b); + if (i != 0) return i; + return ( a < b ? -1 : (a == b ? 0 : 1)); + } + typedef int (*compareFn)(const void*, const void*); } @@ -1166,7 +1180,7 @@ // Use a simple bubble sort for small number of methods since // qsort requires a functional pointer call for each comparison. - if (UseCompressedOops || length < 8) { + if (length < 8) { bool sorted = true; for (int i=length-1; i>0; i--) { for (int j=0; jbase(), length, heapOopSize, compare); } diff -r 96d554193f72 -r 77261afdc5f2 test/runtime/6925573/SortMethodsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/6925573/SortMethodsTest.java Tue May 04 15:12:08 2010 -0400 @@ -0,0 +1,190 @@ +/* + * Copyright 2008-2010 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Arrays; +import java.util.Vector; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +/* + * @test SortMethodsTest + * @bug 6925573 + * @summary verify that class loading does not need quadratic time with regard to the number of class +methods. + * @run main SortMethodsTest + * @author volker.simonis@gmail.com +*/ + +public class SortMethodsTest { + + static String createClass(String name, int nrOfMethods) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.println("public class " + name + "{"); + for (int i = 0; i < nrOfMethods; i++) { + pw.println(" public void m" + i + "() {}"); + } + pw.println(" public static String sayHello() {"); + pw.println(" return \"Hello from class \" + " + name + + ".class.getName() + \" with \" + " + name + + ".class.getDeclaredMethods().length + \" methods\";"); + pw.println(" }"); + pw.println("}"); + pw.close(); + return sw.toString(); + } + + public static void main(String args[]) { + + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diags = new DiagnosticCollector(); + final String cName = new String("ManyMethodsClass"); + Vector results = new Vector(); + + for (int i = 6; i < 600000; i*=10) { + String klass = createClass(cName, i); + JavaMemoryFileObject file = new JavaMemoryFileObject(cName, klass); + MemoryFileManager mfm = new MemoryFileManager(comp.getStandardFileManager(diags, null, null), file); + CompilationTask task = comp.getTask(null, mfm, diags, null, null, Arrays.asList(file)); + + if (task.call()) { + try { + MemoryClassLoader mcl = new MemoryClassLoader(file); + long start = System.nanoTime(); + Class c = Class.forName(cName, true, mcl); + long end = System.nanoTime(); + results.add(end - start); + Method m = c.getDeclaredMethod("sayHello", new Class[0]); + String ret = (String)m.invoke(null, new Object[0]); + System.out.println(ret + " (loaded and resloved in " + (end - start) + "ns)"); + } catch (Exception e) { + System.err.println(e); + } + } + else { + System.out.println(klass); + System.out.println(); + for (Diagnostic diag : diags.getDiagnostics()) { + System.out.println(diag.getCode() + "\n" + diag.getKind() + "\n" + diag.getPosition()); + System.out.println(diag.getSource() + "\n" + diag.getMessage(null)); + } + } + } + + long lastRatio = 0; + for (int i = 2; i < results.size(); i++) { + long normalized1 = Math.max(results.get(i-1) - results.get(0), 1); + long normalized2 = Math.max(results.get(i) - results.get(0), 1); + long ratio = normalized2/normalized1; + lastRatio = ratio; + System.out.println("10 x more methods requires " + ratio + " x more time"); + } + // The following is just vague estimation but seems to work on current x86_64 and sparcv9 machines + if (lastRatio > 80) { + throw new RuntimeException("ATTENTION: it seems that class loading needs quadratic time with regard to the number of class methods!!!"); + } + } +} + +class JavaMemoryFileObject extends SimpleJavaFileObject { + + private final String code; + private ByteArrayOutputStream byteCode; + + JavaMemoryFileObject(String name, String code) { + super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); + this.code = code; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return code; + } + + @Override + public OutputStream openOutputStream() { + byteCode = new ByteArrayOutputStream(); + return byteCode; + } + + byte[] getByteCode() { + return byteCode.toByteArray(); + } +} + +class MemoryClassLoader extends ClassLoader { + + private final JavaMemoryFileObject jfo; + + public MemoryClassLoader(JavaMemoryFileObject jfo) { + this.jfo = jfo; + } + + public Class findClass(String name) { + byte[] b = jfo.getByteCode(); + return defineClass(name, b, 0, b.length); + } +} + +class MemoryFileManager extends ForwardingJavaFileManager { + + private final JavaFileObject jfo; + + public MemoryFileManager(StandardJavaFileManager jfm, JavaFileObject jfo) { + super(jfm); + this.jfo = jfo; + } + + @Override + public FileObject getFileForInput(Location location, String packageName, + String relativeName) throws IOException { + return jfo; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, + Kind kind, FileObject outputFile) throws IOException { + return jfo; + } + +}