/
githubmirror
/
jdk11u-dev
Обзор
Документация
Войти
/
githubmirror
/
jdk11u-dev
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/jdk/java/lang/instrument/RetransformBigClassTest.java
161 строка
7 KB
Chad Rakoczy
8277444: Data race between JvmtiClassFileReconstituter::copy_bytecodes and class linking
13 июл 2026, 17:56
13 июл 2026, 17:56
e5d1afc
Код
Авторство
О чём код?
/* * Copyright Amazon.com Inc. 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 8277444 * * @library /test/lib * @compile SimpleIdentityTransformer.java * @run shell MakeJAR.sh retransformAgent * @run main/othervm -javaagent:retransformAgent.jar RetransformBigClassTest */ import jdk.test.lib.compiler.InMemoryJavaCompiler; /* * JvmtiClassFileReconstituter::copy_bytecodes restores bytecodes rewritten * by the linking process. It is used by RetransformClasses. * JDK-8277444 is a data race between copy_bytecodes and the linking process. * This test puts the linking process in one thread and the retransforming process * in another thread. The test uses Class.forName("BigClass", false, classLoader) * which does not link the class. When the class is used, the linking process starts. * In another thread retransforming of the class is happening. * We generate a class with big methods. A number of methods and their size are * chosen to make the linking and retransforming processes run concurrently. * We delay the retransforming process to follow the linking process. * If there is no synchronization between the processes, a data race will happen. */ public class RetransformBigClassTest extends AInstrumentationTestCase { private static final Object LOCK = new Object(); private static final int COUNTER_INC_COUNT = 2000; // A number of 'c+=1;' statements in methods of a class. private static final int MIN_LINK_TIME_MS = 60; // Large enough so the linking and retransforming processes run in parallel. private static final int RETRANSFORM_CLASSES_DELAY_MS = 37; // We manage to create a data race when a delay is in the range 0.52x - 0.62x of MIN_LINK_TIME_MS. private static Class<?> bigClass; private static byte[] bigClassBytecode; private Thread retransformThread; RetransformBigClassTest() { super("RetransformBigClassTest"); } public static void main(String[] args) throws Throwable { new RetransformBigClassTest().runTest(); } protected final void doRunTest() throws Throwable { ClassLoader classLoader = new ClassLoader() { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (name.equals("BigClass")) { return defineClass(name, bigClassBytecode, 0, bigClassBytecode.length); } return super.findClass(name); } }; synchronized (LOCK) { bigClass = Class.forName("BigClass", false, classLoader); LOCK.notify(); } // Make a use of the BigClass assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0); retransformThread.join(); } private byte[] createClassBytecode(String className, int methodCount) throws Exception { String methodBody = ""; for (int j = 0; j < COUNTER_INC_COUNT; j++) { methodBody += "c+=1;"; } String classSrc = "public class " + className + " { int c;"; for (int i = 0; i < methodCount; i++) { classSrc += "\npublic void m" + i + "(){"; classSrc += methodBody; classSrc += "\n}"; } classSrc += "\n}"; return InMemoryJavaCompiler.compile(className, classSrc); } // We need a number of methods such that the linking time is greater than // or equal to MIN_LINK_TIME_MS. // We create a class having 5 methods and trigger the linking process. // We measure the time taken and use it to calculate the needed number. private int findMethodCount() throws Exception { int methodCount = 5; final String className = "BigClass" + methodCount; final byte[] bytecode = createClassBytecode(className, methodCount); ClassLoader classLoader = new ClassLoader() { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (name.equals(className)) { return defineClass(name, bytecode, 0, bytecode.length); } return super.findClass(name); } }; var bigClass = Class.forName(className, false, classLoader); long startTime = System.nanoTime(); assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0); double linkTimeMs = (System.nanoTime() - startTime) / 1000000.0; System.out.println("Link time for a class with " + methodCount + " methods each having " + COUNTER_INC_COUNT + " counter increments: " + Math.round(linkTimeMs)); if (linkTimeMs < MIN_LINK_TIME_MS) { methodCount = (int)Math.round((MIN_LINK_TIME_MS * methodCount) / linkTimeMs); } System.out.println("The number of methods to exceed " + MIN_LINK_TIME_MS + " ms linking time: " + methodCount); return methodCount; } @Override protected void setUp() throws Exception { super.setUp(); bigClassBytecode = createClassBytecode("BigClass", findMethodCount()); fInst.addTransformer(new SimpleIdentityTransformer()); retransformThread = new Thread(() -> { try { synchronized (LOCK) { while (bigClass == null) { System.out.println("[retransformThread]: Waiting for bigClass"); LOCK.wait(); } } Thread.sleep(RETRANSFORM_CLASSES_DELAY_MS); fInst.retransformClasses(bigClass); } catch (Exception e) { e.printStackTrace(); } }); retransformThread.start(); Thread.sleep(100); } }