8231125: Improve testing of parallel loading of shared classes by the boot class loader

Reviewed-by: ccheung, coleenp, dholmes
This commit is contained in:
Ioi Lam 2019-09-22 17:16:35 -07:00
parent 83f41d2736
commit 9c92ecba04
4 changed files with 75 additions and 31 deletions

View file

@ -1270,6 +1270,7 @@ InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik,
TRAPS) { TRAPS) {
if (ik != NULL) { if (ik != NULL) {
assert(!ik->is_unshareable_info_restored(), "shared class can be loaded only once");
Symbol* class_name = ik->name(); Symbol* class_name = ik->name();
bool visible = is_shared_class_visible( bool visible = is_shared_class_visible(

View file

@ -524,6 +524,18 @@ protected:
virtual void remove_java_mirror(); virtual void remove_java_mirror();
virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);
bool is_unshareable_info_restored() const {
assert(is_shared(), "use this for shared classes only");
if (has_raw_archived_mirror()) {
// _java_mirror is not a valid OopHandle but rather an encoded reference in the shared heap
return false;
} else if (_java_mirror.ptr_raw() == NULL) {
return false;
} else {
return true;
}
}
public: public:
// subclass accessor (here for convenience; undefined for non-klass objects) // subclass accessor (here for convenience; undefined for non-klass objects)
virtual bool is_leaf_class() const { fatal("not a class"); return false; } virtual bool is_leaf_class() const { fatal("not a class"); return false; }

View file

@ -36,10 +36,34 @@
public class ParallelLoadTest { public class ParallelLoadTest {
public static final int MAX_CLASSES = 40; public static final int MAX_CLASSES = 40;
/* For easy stress testing, do this:
i=0; while jtreg -DParallelLoadTest.app.loops=100 -DParallelLoadTest.boot.loops=100 \
ParallelLoadTest.java; do i=$(expr $i + 1); echo =====$i; done
*/
private static final int APP_LOOPS = Integer.parseInt(System.getProperty("ParallelLoadTest.app.loops", "1"));
private static final int BOOT_LOOPS = Integer.parseInt(System.getProperty("ParallelLoadTest.boot.loops", "1"));
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
JarBuilder.build("parallel_load", getClassList(true)); JarBuilder.build("parallel_load", getClassList(true));
String appJar = TestCommon.getTestJar("parallel_load.jar"); String testJar = TestCommon.getTestJar("parallel_load.jar");
TestCommon.test(appJar, getClassList(false), "ParallelLoad");
// (1) Load the classes from app class loader
TestCommon.testDump(testJar, getClassList(false));
for (int i = 0; i < APP_LOOPS; i++) {
TestCommon.run("-cp", testJar, "ParallelLoad").assertNormalExit();
}
// (2) Load the classes from boot class loader
String bootcp = "-Xbootclasspath/a:" + testJar;
TestCommon.testDump(null, getClassList(false), bootcp);
for (int i = 0; i < BOOT_LOOPS; i++) {
TestCommon.run(bootcp,
// "-Xlog:class+load=debug",
"ParallelLoad").assertNormalExit();
}
} }
private static String[] getClassList(boolean includeWatchdog) { private static String[] getClassList(boolean includeWatchdog) {
@ -47,7 +71,7 @@ public class ParallelLoadTest {
String[] classList = new String[MAX_CLASSES + extra]; String[] classList = new String[MAX_CLASSES + extra];
int i; int i;
for (i=0; i<MAX_CLASSES; i++) { for (i = 0; i < MAX_CLASSES; i++) {
classList[i] = "ParallelClass" + i; classList[i] = "ParallelClass" + i;
} }

View file

@ -25,6 +25,7 @@
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
// This test helper is parameterized by: // This test helper is parameterized by:
@ -78,7 +79,7 @@ public class ParallelLoad {
if ("FINGERPRINT_MODE".equals(args[1])) { if ("FINGERPRINT_MODE".equals(args[1])) {
mode = FINGERPRINT_MODE; mode = FINGERPRINT_MODE;
classLoaders = new ClassLoader[NUM_THREADS]; classLoaders = new ClassLoader[NUM_THREADS];
for (int i=0; i<NUM_THREADS; i++) { for (int i = 0; i < NUM_THREADS; i++) {
URL url = new File(customJar).toURI().toURL(); URL url = new File(customJar).toURI().toURL();
URL[] urls = new URL[] {url}; URL[] urls = new URL[] {url};
classLoaders[i] = new URLClassLoader(urls); classLoaders[i] = new URLClassLoader(urls);
@ -93,7 +94,7 @@ public class ParallelLoad {
System.out.println("Start Parallel Load ..."); System.out.println("Start Parallel Load ...");
Thread thread[] = new Thread[NUM_THREADS]; Thread thread[] = new Thread[NUM_THREADS];
for (int i=0; i<NUM_THREADS; i++) { for (int i = 0; i < NUM_THREADS; i++) {
Thread t = new ParallelLoadThread(i); Thread t = new ParallelLoadThread(i);
t.start(); t.start();
thread[i] = t; thread[i] = t;
@ -103,7 +104,7 @@ public class ParallelLoad {
watchdog.setDaemon(true); watchdog.setDaemon(true);
watchdog.start(); watchdog.start();
for (int i=0; i<NUM_THREADS; i++) { for (int i = 0; i < NUM_THREADS; i++) {
thread[i].join(); thread[i].join();
} }
System.out.println("Parallel Load ... done"); System.out.println("Parallel Load ... done");
@ -128,8 +129,13 @@ class ParallelLoadWatchdog extends Thread {
class ParallelLoadThread extends Thread { class ParallelLoadThread extends Thread {
static int num_ready[] = new int[ParallelLoad.MAX_CLASSES]; static AtomicInteger num_ready[];
static Object lock = new Object(); static {
num_ready = new AtomicInteger[ParallelLoad.MAX_CLASSES];
for (int i = 0; i < ParallelLoad.MAX_CLASSES; i++) {
num_ready[i] = new AtomicInteger();
}
}
static String transformMode = static String transformMode =
System.getProperty("appcds.parallel.transform.mode", "none"); System.getProperty("appcds.parallel.transform.mode", "none");
@ -153,21 +159,21 @@ class ParallelLoadThread extends Thread {
} }
private void run0() throws Throwable { private void run0() throws Throwable {
for (int i=0; i<ParallelLoad.MAX_CLASSES; i++) { for (int i = 0; i < ParallelLoad.MAX_CLASSES; i++) {
synchronized(lock) {
num_ready[i] ++;
while (num_ready[i] < ParallelLoad.NUM_THREADS) {
lock.wait();
}
lock.notifyAll();
}
log("this = %s %d", this, i);
String className = "ParallelClass" + i; String className = "ParallelClass" + i;
if (transformMode.equals("cflh")) if (transformMode.equals("cflh")) {
className = "ParallelClassTr" + i; className = "ParallelClassTr" + i;
}
Class clazz = null; Class clazz = null;
// Spin until every thread is ready to proceed
num_ready[i].incrementAndGet();
while (num_ready[i].intValue() < ParallelLoad.NUM_THREADS) {
;
}
{ // Avoid logging in this block so the threads can proceed without
// waiting for the stdout lock, etc.
switch (ParallelLoad.loaderType) { switch (ParallelLoad.loaderType) {
case ParallelLoad.SYSTEM_LOADER: case ParallelLoad.SYSTEM_LOADER:
clazz = Class.forName(className); clazz = Class.forName(className);
@ -179,10 +185,11 @@ class ParallelLoadThread extends Thread {
clazz = ParallelLoad.classLoaders[thread_id].loadClass(className); clazz = ParallelLoad.classLoaders[thread_id].loadClass(className);
break; break;
} }
log("clazz = %s", clazz);
testTransformation(clazz); testTransformation(clazz);
} }
log("thread[%d] t = %s, c = %s, l = %s", thread_id, this, clazz, clazz.getClassLoader());
}
} }
private void testTransformation(Class c) throws Exception { private void testTransformation(Class c) throws Exception {