diff --git a/src/hotspot/share/prims/resolvedMethodTable.cpp b/src/hotspot/share/prims/resolvedMethodTable.cpp index 5b9b07c5842..cdd41730dea 100644 --- a/src/hotspot/share/prims/resolvedMethodTable.cpp +++ b/src/hotspot/share/prims/resolvedMethodTable.cpp @@ -175,7 +175,13 @@ oop ResolvedMethodTable::find_method(const Method* method) { ResolvedMethodTableLookup lookup(thread, method_hash(method), method); ResolvedMethodGet rmg(thread, method); - _local_table->get(thread, lookup, rmg); + bool rehash_warning = false; + _local_table->get(thread, lookup, rmg, &rehash_warning); + if (rehash_warning) { + // if load factor is low but we need to rehash that's a problem with the hash function. + log_info(membername, table)("Rehash warning, load factor %g", get_load_factor()); + trigger_concurrent_work(); + } return rmg.get_res_oop(); } diff --git a/test/hotspot/jtreg/runtime/MemberName/ResolvedMethodTableHash.java b/test/hotspot/jtreg/runtime/MemberName/ResolvedMethodTableHash.java index 8abc4d41b16..9ee6a808e24 100644 --- a/test/hotspot/jtreg/runtime/MemberName/ResolvedMethodTableHash.java +++ b/test/hotspot/jtreg/runtime/MemberName/ResolvedMethodTableHash.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -25,7 +25,11 @@ * @test * @bug 8249719 * @summary ResolvedMethodTable hash function should take method class into account - * @run main/othervm/manual -Xmx256m -XX:MaxMetaspaceSize=256m ResolvedMethodTableHash 200000 + * @requires vm.flagless + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver ResolvedMethodTableHash */ import java.lang.invoke.MethodHandle; @@ -35,34 +39,38 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -// The test generates thousands MethodHandles to the methods of the same name -// and the same signature. This should not take too long, unless Method hash +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +// The test generates a thousand MethodHandles to the methods of the same name +// and the same signature. The rehash warning shouldn't be returned, unless Method hash // function takes only the name and the signature as an input. -public class ResolvedMethodTableHash extends ClassLoader { +public class ResolvedMethodTableHash { - // Generate a MethodHandle for ClassName.m() - private MethodHandle generate(String className) throws ReflectiveOperationException { - byte[] buf = new byte[100]; - int size = writeClass(buf, className); - Class cls = defineClass(null, buf, 0, size); - return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); - } + public static class ResolvedMethodTableHashTest extends ClassLoader { + // Generate a MethodHandle for ClassName.m() + private MethodHandle generate(String className) throws ReflectiveOperationException { + byte[] buf = new byte[100]; + int size = writeClass(buf, className); + Class cls = defineClass(null, buf, 0, size); + return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); + } - private MethodHandle generateWithSameName() throws ReflectiveOperationException { - byte[] buf = new byte[100]; - int size = writeClass(buf, "MH$$"); - // use different classloader instances to load the classes with the same name - Class cls = new ResolvedMethodTableHash().defineClass(null, buf, 0, size); - return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); - } + private MethodHandle generateWithSameName() throws ReflectiveOperationException { + byte[] buf = new byte[100]; + int size = writeClass(buf, "MH$$"); + // use different classloader instances to load the classes with the same name + Class cls = new ResolvedMethodTableHashTest().defineClass(null, buf, 0, size); + return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); + } - // Produce a class file with the given name and a single method: - // public static native void m(); - private int writeClass(byte[] buf, String className) { - return ByteBuffer.wrap(buf) - .putInt(0xCAFEBABE) // magic - .putInt(50) // version: 50 - .putShort((short) 7) // constant_pool_count: 7 + // Produce a class file with the given name and a single method: + // public static native void m(); + private int writeClass(byte[] buf, String className) { + return ByteBuffer.wrap(buf) + .putInt(0xCAFEBABE) // magic + .putInt(50) // version: 50 + .putShort((short) 7) // constant_pool_count: 7 .put((byte) 7).putShort((short) 2) .put((byte) 1).putShort((short) className.length()).put(className.getBytes()) .put((byte) 7).putShort((short) 4) @@ -70,37 +78,50 @@ public class ResolvedMethodTableHash extends ClassLoader { .put((byte) 1).putShort((short) 1).put("m".getBytes()) .put((byte) 1).putShort((short) 3).put("()V".getBytes()) .putShort((short) 0x21) // access_flags: public super - .putShort((short) 1) // this_class: #1 - .putShort((short) 3) // super_class: #3 - .putShort((short) 0) // interfaces_count: 0 - .putShort((short) 0) // fields_count: 0 - .putShort((short) 1) // methods_count: 1 + .putShort((short) 1) // this_class: #1 + .putShort((short) 3) // super_class: #3 + .putShort((short) 0) // interfaces_count: 0 + .putShort((short) 0) // fields_count: 0 + .putShort((short) 1) // methods_count: 1 .putShort((short) 0x109) // access_flags: public static native - .putShort((short) 5) // name_index: #5 - .putShort((short) 6) // descriptor_index: #6 - .putShort((short) 0) // attributes_count: 0 - .putShort((short) 0) // attributes_count: 0 + .putShort((short) 5) // name_index: #5 + .putShort((short) 6) // descriptor_index: #6 + .putShort((short) 0) // attributes_count: 0 + .putShort((short) 0) // attributes_count: 0 .position(); + } + + public static void main(String[] args) throws Exception { + + ResolvedMethodTableHashTest generator = new ResolvedMethodTableHashTest(); + List handles = new ArrayList<>(); + + int count = 1001; + + for (int i = 0; i < count; i++) { + // prevents metaspace oom + if (i % 20 != 0) { + handles.add(generator.generate("MH$" + i)); + } else { + handles.add(generator.generateWithSameName()); + } + if (i % 1000 == 0) { + System.out.println("Generated " + i + " handles"); + } + } + + System.out.println("Test passed"); + } } public static void main(String[] args) throws Exception { - ResolvedMethodTableHash generator = new ResolvedMethodTableHash(); - List handles = new ArrayList<>(); - int count = args.length > 0 ? Integer.parseInt(args[0]) : 200000; - - for (int i = 0; i < count; i++) { - // prevents metaspace oom - if (i % 20 != 0) { - handles.add(generator.generate("MH$" + i)); - } else { - handles.add(generator.generateWithSameName()); - } - if (i % 1000 == 0) { - System.out.println("Generated " + i + " handles"); - } - } - - System.out.println("Test passed"); + // Running the table with only 1000 entries should not provoke a needs rehash warning, unless the hash code is bad. + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:membername+table", + ResolvedMethodTableHashTest.class.getName()); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("[membername,table] Rehash warning, load factor"); + output.shouldContain("Generated 1000 handles"); + output.shouldHaveExitValue(0); } }