/* * Copyright (c) 2013, 2018, 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 * * @summary converted from VM Testbase metaspace/stressDictionary. * VM Testbase keywords: [nonconcurrent, javac] * * @library /vmTestbase /test/lib * @run driver jdk.test.lib.FileInstaller . . * @run main/othervm metaspace.stressDictionary.StressDictionary */ package metaspace.stressDictionary; import java.util.*; import java.lang.management.ManagementFactory; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import nsk.share.gc.GCTestBase; import nsk.share.test.*; import vm.share.InMemoryJavaCompiler; /** * There is a data structure named "dictionary" in class BlockFreelist. It stores * information about free memory blocks for further reusing. Allocation of new block goes * from dictionary only if dictionary is fat enough. (At the moment of test creation this limit is 64K.) * So to stress dictionary we should fill it permanently. The easiest way to fill the dictionary * is to fail class loading. This failed action will return allocated blocks to dictionary. * * There are two type of threads in this test: threads, failing classloading and threads, * loading regular classes and checking they work properly. */ public class StressDictionary extends GCTestBase { private class FillingDictionaryWorker implements Callable { @Override public Object call() throws Exception { while (stresser.continueExecution()) { try { byte[] bytecode = generateAndCompile(); bytecode[random.nextInt(bytecode.length)] = (byte) 42; classloader.define(bytecode); changeClassloaderIfRequired(); } catch (Throwable e) { // We can get ClassFormatError, ClassNotFoundException or anything else here } } return null; } } private class RegularWorker implements Callable { @Override public Object call() throws Exception { while (stresser.continueExecution()) { byte[] bytecode = generateAndCompile(); Class c = classloader.define(bytecode); testClass(c); changeClassloaderIfRequired(); } return null; } } private static String[] args; private static final int DROP_CLASSLOADER_LIMIT = 50000; private static final String methodName = "myMethod"; private static final int NUMBER_OF_CORRUPTING_THREADS = 10; private static final int NUMBER_OF_METHOD_CALLS = 50; private static final int NUMBER_OF_NOT_CORRUPTING_THREADS = 10; private AtomicLong classesCounter = new AtomicLong(0); private volatile ClassloaderUnderTest classloader = new ClassloaderUnderTest(); private Random random; private ExecutionController stresser; public static void main(String[] args) { StressDictionary.args = args; Tests.runTest(new StressDictionary(), args); } public void run() { random = new Random(runParams.getSeed()); stresser = new Stresser(args); stresser.start(1); List> tasks = new LinkedList>(); for (int i = 0; i < NUMBER_OF_CORRUPTING_THREADS; i++) { tasks.add(this.new FillingDictionaryWorker()); } for (int i = 0; i < NUMBER_OF_NOT_CORRUPTING_THREADS; i++) { tasks.add(this.new RegularWorker()); } ExecutorService executorService = Executors.newCachedThreadPool(); try { executorService.invokeAll(tasks); } catch (InterruptedException e) { e.printStackTrace(); } } private void changeClassloaderIfRequired() { if (ManagementFactory.getClassLoadingMXBean().getLoadedClassCount() > DROP_CLASSLOADER_LIMIT) { ClassloaderUnderTest newOne = new ClassloaderUnderTest(); classloader = newOne; } } private byte[] generateAndCompile() { Map sources = new HashMap(); String className = "MyClass" + classesCounter.incrementAndGet(); sources.put(className, generateSource(className)); return InMemoryJavaCompiler.compile(sources).values().iterator().next(); } private CharSequence generateSource(String className) { return "public class " + className + " { " + "public static String s1 = \"s1" + random.nextInt() + "\"; " + "public String s2 = \"s2" + random.nextInt() + "\"; " + "public String " + methodName + "() {return s1 + s2; } " + "}"; } private void testClass(Class clazz) { try { for (Method m : clazz.getMethods()) { if (m.getName().equals(methodName)) { for (int j = 0; j < NUMBER_OF_METHOD_CALLS; j++) { m.invoke(clazz.newInstance()); } } } } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { log.error("Class check failed: " + e.getMessage()); e.printStackTrace(); setFailed(true); } } }