|
1 | 1 | /* |
2 | | - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
|
25 | 25 | * @test |
26 | 26 | * @bug 8249719 |
27 | 27 | * @summary ResolvedMethodTable hash function should take method class into account |
28 | | - * @run main/othervm/manual -Xmx256m -XX:MaxMetaspaceSize=256m ResolvedMethodTableHash 200000 |
| 28 | + * @requires vm.flagless |
| 29 | + * @library /test/lib |
| 30 | + * @modules java.base/jdk.internal.misc |
| 31 | + * java.management |
| 32 | + * @run driver ResolvedMethodTableHash |
29 | 33 | */ |
30 | 34 |
|
31 | 35 | import java.lang.invoke.MethodHandle; |
|
35 | 39 | import java.util.ArrayList; |
36 | 40 | import java.util.List; |
37 | 41 |
|
38 | | -// The test generates thousands MethodHandles to the methods of the same name |
39 | | -// and the same signature. This should not take too long, unless Method hash |
| 42 | +import jdk.test.lib.process.OutputAnalyzer; |
| 43 | +import jdk.test.lib.process.ProcessTools; |
| 44 | + |
| 45 | +// The test generates a thousand MethodHandles to the methods of the same name |
| 46 | +// and the same signature. The rehash warning shouldn't be returned, unless Method hash |
40 | 47 | // function takes only the name and the signature as an input. |
41 | | -public class ResolvedMethodTableHash extends ClassLoader { |
| 48 | +public class ResolvedMethodTableHash { |
42 | 49 |
|
43 | | - // Generate a MethodHandle for ClassName.m() |
44 | | - private MethodHandle generate(String className) throws ReflectiveOperationException { |
45 | | - byte[] buf = new byte[100]; |
46 | | - int size = writeClass(buf, className); |
47 | | - Class<?> cls = defineClass(null, buf, 0, size); |
48 | | - return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); |
49 | | - } |
| 50 | + public static class ResolvedMethodTableHashTest extends ClassLoader { |
| 51 | + // Generate a MethodHandle for ClassName.m() |
| 52 | + private MethodHandle generate(String className) throws ReflectiveOperationException { |
| 53 | + byte[] buf = new byte[100]; |
| 54 | + int size = writeClass(buf, className); |
| 55 | + Class<?> cls = defineClass(null, buf, 0, size); |
| 56 | + return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); |
| 57 | + } |
50 | 58 |
|
51 | | - private MethodHandle generateWithSameName() throws ReflectiveOperationException { |
52 | | - byte[] buf = new byte[100]; |
53 | | - int size = writeClass(buf, "MH$$"); |
54 | | - // use different classloader instances to load the classes with the same name |
55 | | - Class<?> cls = new ResolvedMethodTableHash().defineClass(null, buf, 0, size); |
56 | | - return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); |
57 | | - } |
| 59 | + private MethodHandle generateWithSameName() throws ReflectiveOperationException { |
| 60 | + byte[] buf = new byte[100]; |
| 61 | + int size = writeClass(buf, "MH$$"); |
| 62 | + // use different classloader instances to load the classes with the same name |
| 63 | + Class<?> cls = new ResolvedMethodTableHashTest().defineClass(null, buf, 0, size); |
| 64 | + return MethodHandles.publicLookup().findStatic(cls, "m", MethodType.methodType(void.class)); |
| 65 | + } |
58 | 66 |
|
59 | | - // Produce a class file with the given name and a single method: |
60 | | - // public static native void m(); |
61 | | - private int writeClass(byte[] buf, String className) { |
62 | | - return ByteBuffer.wrap(buf) |
63 | | - .putInt(0xCAFEBABE) // magic |
64 | | - .putInt(50) // version: 50 |
65 | | - .putShort((short) 7) // constant_pool_count: 7 |
| 67 | + // Produce a class file with the given name and a single method: |
| 68 | + // public static native void m(); |
| 69 | + private int writeClass(byte[] buf, String className) { |
| 70 | + return ByteBuffer.wrap(buf) |
| 71 | + .putInt(0xCAFEBABE) // magic |
| 72 | + .putInt(50) // version: 50 |
| 73 | + .putShort((short) 7) // constant_pool_count: 7 |
66 | 74 | .put((byte) 7).putShort((short) 2) |
67 | 75 | .put((byte) 1).putShort((short) className.length()).put(className.getBytes()) |
68 | 76 | .put((byte) 7).putShort((short) 4) |
69 | 77 | .put((byte) 1).putShort((short) 16).put("java/lang/Object".getBytes()) |
70 | 78 | .put((byte) 1).putShort((short) 1).put("m".getBytes()) |
71 | 79 | .put((byte) 1).putShort((short) 3).put("()V".getBytes()) |
72 | 80 | .putShort((short) 0x21) // access_flags: public super |
73 | | - .putShort((short) 1) // this_class: #1 |
74 | | - .putShort((short) 3) // super_class: #3 |
75 | | - .putShort((short) 0) // interfaces_count: 0 |
76 | | - .putShort((short) 0) // fields_count: 0 |
77 | | - .putShort((short) 1) // methods_count: 1 |
| 81 | + .putShort((short) 1) // this_class: #1 |
| 82 | + .putShort((short) 3) // super_class: #3 |
| 83 | + .putShort((short) 0) // interfaces_count: 0 |
| 84 | + .putShort((short) 0) // fields_count: 0 |
| 85 | + .putShort((short) 1) // methods_count: 1 |
78 | 86 | .putShort((short) 0x109) // access_flags: public static native |
79 | | - .putShort((short) 5) // name_index: #5 |
80 | | - .putShort((short) 6) // descriptor_index: #6 |
81 | | - .putShort((short) 0) // attributes_count: 0 |
82 | | - .putShort((short) 0) // attributes_count: 0 |
| 87 | + .putShort((short) 5) // name_index: #5 |
| 88 | + .putShort((short) 6) // descriptor_index: #6 |
| 89 | + .putShort((short) 0) // attributes_count: 0 |
| 90 | + .putShort((short) 0) // attributes_count: 0 |
83 | 91 | .position(); |
84 | | - } |
| 92 | + } |
85 | 93 |
|
86 | | - public static void main(String[] args) throws Exception { |
87 | | - ResolvedMethodTableHash generator = new ResolvedMethodTableHash(); |
88 | | - List<MethodHandle> handles = new ArrayList<>(); |
| 94 | + public static void main(String[] args) throws Exception { |
89 | 95 |
|
90 | | - int count = args.length > 0 ? Integer.parseInt(args[0]) : 200000; |
| 96 | + ResolvedMethodTableHashTest generator = new ResolvedMethodTableHashTest(); |
| 97 | + List<MethodHandle> handles = new ArrayList<>(); |
91 | 98 |
|
92 | | - for (int i = 0; i < count; i++) { |
93 | | - // prevents metaspace oom |
94 | | - if (i % 20 != 0) { |
95 | | - handles.add(generator.generate("MH$" + i)); |
96 | | - } else { |
97 | | - handles.add(generator.generateWithSameName()); |
98 | | - } |
99 | | - if (i % 1000 == 0) { |
100 | | - System.out.println("Generated " + i + " handles"); |
| 99 | + int count = 1001; |
| 100 | + |
| 101 | + for (int i = 0; i < count; i++) { |
| 102 | + // prevents metaspace oom |
| 103 | + if (i % 20 != 0) { |
| 104 | + handles.add(generator.generate("MH$" + i)); |
| 105 | + } else { |
| 106 | + handles.add(generator.generateWithSameName()); |
| 107 | + } |
| 108 | + if (i % 1000 == 0) { |
| 109 | + System.out.println("Generated " + i + " handles"); |
| 110 | + } |
101 | 111 | } |
| 112 | + |
| 113 | + System.out.println("Test passed"); |
102 | 114 | } |
| 115 | + } |
| 116 | + |
| 117 | + public static void main(String[] args) throws Exception { |
103 | 118 |
|
104 | | - System.out.println("Test passed"); |
| 119 | + // Running the table with only 1000 entries should not provoke a needs rehash warning, unless the hash code is bad. |
| 120 | + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:membername+table", |
| 121 | + ResolvedMethodTableHashTest.class.getName()); |
| 122 | + OutputAnalyzer output = new OutputAnalyzer(pb.start()); |
| 123 | + output.shouldNotContain("[membername,table] Rehash warning, load factor"); |
| 124 | + output.shouldContain("Generated 1000 handles"); |
| 125 | + output.shouldHaveExitValue(0); |
105 | 126 | } |
106 | 127 | } |
0 commit comments