8210094: Better loading of classloader classes
Reviewed-by: acorn, hseigel, ahgross, rhalade
This commit is contained in:
parent
90aa9d02a4
commit
e66dfd30c4
@ -4484,33 +4484,6 @@ void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach super classes and interface classes to class loader data
|
|
||||||
static void record_defined_class_dependencies(const InstanceKlass* defined_klass,
|
|
||||||
TRAPS) {
|
|
||||||
assert(defined_klass != NULL, "invariant");
|
|
||||||
|
|
||||||
ClassLoaderData* const defining_loader_data = defined_klass->class_loader_data();
|
|
||||||
if (defining_loader_data->is_the_null_class_loader_data()) {
|
|
||||||
// Dependencies to null class loader data are implicit.
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// add super class dependency
|
|
||||||
Klass* const super = defined_klass->super();
|
|
||||||
if (super != NULL) {
|
|
||||||
defining_loader_data->record_dependency(super);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add super interface dependencies
|
|
||||||
const Array<InstanceKlass*>* const local_interfaces = defined_klass->local_interfaces();
|
|
||||||
if (local_interfaces != NULL) {
|
|
||||||
const int length = local_interfaces->length();
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
defining_loader_data->record_dependency(local_interfaces->at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility methods for appending an array with check for duplicates
|
// utility methods for appending an array with check for duplicates
|
||||||
|
|
||||||
static void append_interfaces(GrowableArray<InstanceKlass*>* result,
|
static void append_interfaces(GrowableArray<InstanceKlass*>* result,
|
||||||
@ -5717,9 +5690,6 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the loader_data graph.
|
|
||||||
record_defined_class_dependencies(ik, CHECK);
|
|
||||||
|
|
||||||
ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
|
ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
|
||||||
|
|
||||||
if (!is_internal()) {
|
if (!is_internal()) {
|
||||||
|
@ -561,11 +561,6 @@ void ClassLoaderDataGraph::clean_module_and_package_info() {
|
|||||||
|
|
||||||
ClassLoaderData* data = _head;
|
ClassLoaderData* data = _head;
|
||||||
while (data != NULL) {
|
while (data != NULL) {
|
||||||
// Remove entries in the dictionary of live class loader that have
|
|
||||||
// initiated loading classes in a dead class loader.
|
|
||||||
if (data->dictionary() != NULL) {
|
|
||||||
data->dictionary()->do_unloading();
|
|
||||||
}
|
|
||||||
// Walk a ModuleEntry's reads, and a PackageEntry's exports
|
// Walk a ModuleEntry's reads, and a PackageEntry's exports
|
||||||
// lists to determine if there are modules on those lists that are now
|
// lists to determine if there are modules on those lists that are now
|
||||||
// dead and should be removed. A module's life cycle is equivalent
|
// dead and should be removed. A module's life cycle is equivalent
|
||||||
|
@ -233,40 +233,6 @@ void Dictionary::clean_cached_protection_domains(DictionaryEntry* probe) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Dictionary::do_unloading() {
|
|
||||||
assert_locked_or_safepoint(SystemDictionary_lock);
|
|
||||||
|
|
||||||
// The NULL class loader doesn't initiate loading classes from other class loaders
|
|
||||||
if (loader_data() == ClassLoaderData::the_null_class_loader_data()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove unloaded entries and classes from this dictionary
|
|
||||||
DictionaryEntry* probe = NULL;
|
|
||||||
for (int index = 0; index < table_size(); index++) {
|
|
||||||
for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) {
|
|
||||||
probe = *p;
|
|
||||||
InstanceKlass* ik = probe->instance_klass();
|
|
||||||
ClassLoaderData* k_def_class_loader_data = ik->class_loader_data();
|
|
||||||
|
|
||||||
// If the klass that this loader initiated is dead,
|
|
||||||
// (determined by checking the defining class loader)
|
|
||||||
// remove this entry.
|
|
||||||
if (k_def_class_loader_data->is_unloading()) {
|
|
||||||
assert(k_def_class_loader_data != loader_data(),
|
|
||||||
"cannot have live defining loader and unreachable klass");
|
|
||||||
*p = probe->next();
|
|
||||||
free_entry(probe);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Clean pd_set
|
|
||||||
clean_cached_protection_domains(probe);
|
|
||||||
p = probe->next_addr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just the classes from defining class loaders
|
// Just the classes from defining class loaders
|
||||||
void Dictionary::classes_do(void f(InstanceKlass*)) {
|
void Dictionary::classes_do(void f(InstanceKlass*)) {
|
||||||
for (int index = 0; index < table_size(); index++) {
|
for (int index = 0; index < table_size(); index++) {
|
||||||
|
@ -74,9 +74,6 @@ public:
|
|||||||
|
|
||||||
void unlink();
|
void unlink();
|
||||||
|
|
||||||
// Unload classes whose defining loaders are unloaded
|
|
||||||
void do_unloading();
|
|
||||||
|
|
||||||
// Protection domains
|
// Protection domains
|
||||||
InstanceKlass* find(unsigned int hash, Symbol* name, Handle protection_domain);
|
InstanceKlass* find(unsigned int hash, Symbol* name, Handle protection_domain);
|
||||||
bool is_valid_protection_domain(unsigned int hash,
|
bool is_valid_protection_domain(unsigned int hash,
|
||||||
|
@ -861,8 +861,15 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||||||
check_constraints(d_hash, k, class_loader, false, THREAD);
|
check_constraints(d_hash, k, class_loader, false, THREAD);
|
||||||
|
|
||||||
// Need to check for a PENDING_EXCEPTION again; check_constraints
|
// Need to check for a PENDING_EXCEPTION again; check_constraints
|
||||||
// can throw and doesn't use the CHECK macro.
|
// can throw but we may have to remove entry from the placeholder table below.
|
||||||
if (!HAS_PENDING_EXCEPTION) {
|
if (!HAS_PENDING_EXCEPTION) {
|
||||||
|
// Record dependency for non-parent delegation.
|
||||||
|
// This recording keeps the defining class loader of the klass (k) found
|
||||||
|
// from being unloaded while the initiating class loader is loaded
|
||||||
|
// even if the reference to the defining class loader is dropped
|
||||||
|
// before references to the initiating class loader.
|
||||||
|
loader_data->record_dependency(k);
|
||||||
|
|
||||||
{ // Grabbing the Compile_lock prevents systemDictionary updates
|
{ // Grabbing the Compile_lock prevents systemDictionary updates
|
||||||
// during compilations.
|
// during compilations.
|
||||||
MutexLocker mu(Compile_lock, THREAD);
|
MutexLocker mu(Compile_lock, THREAD);
|
||||||
@ -2177,6 +2184,7 @@ void SystemDictionary::update_dictionary(unsigned int d_hash,
|
|||||||
InstanceKlass* sd_check = find_class(d_hash, name, dictionary);
|
InstanceKlass* sd_check = find_class(d_hash, name, dictionary);
|
||||||
if (sd_check == NULL) {
|
if (sd_check == NULL) {
|
||||||
dictionary->add_klass(d_hash, name, k);
|
dictionary->add_klass(d_hash, name, k);
|
||||||
|
|
||||||
notice_modification();
|
notice_modification();
|
||||||
}
|
}
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
@ -50,7 +50,6 @@ bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Sym
|
|||||||
Klass* this_class = SystemDictionary::resolve_or_fail(
|
Klass* this_class = SystemDictionary::resolve_or_fail(
|
||||||
name, Handle(THREAD, klass->class_loader()),
|
name, Handle(THREAD, klass->class_loader()),
|
||||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||||
klass->class_loader_data()->record_dependency(this_class);
|
|
||||||
if (log_is_enabled(Debug, class, resolve)) {
|
if (log_is_enabled(Debug, class, resolve)) {
|
||||||
Verifier::trace_class_resolution(this_class, klass);
|
Verifier::trace_class_resolution(this_class, klass);
|
||||||
}
|
}
|
||||||
@ -68,7 +67,6 @@ bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Sym
|
|||||||
Klass* from_class = SystemDictionary::resolve_or_fail(
|
Klass* from_class = SystemDictionary::resolve_or_fail(
|
||||||
from_name, Handle(THREAD, klass->class_loader()),
|
from_name, Handle(THREAD, klass->class_loader()),
|
||||||
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
|
||||||
klass->class_loader_data()->record_dependency(from_class);
|
|
||||||
if (log_is_enabled(Debug, class, resolve)) {
|
if (log_is_enabled(Debug, class, resolve)) {
|
||||||
Verifier::trace_class_resolution(from_class, klass);
|
Verifier::trace_class_resolution(from_class, klass);
|
||||||
}
|
}
|
||||||
|
@ -2015,7 +2015,6 @@ Klass* ClassVerifier::load_class(Symbol* name, TRAPS) {
|
|||||||
true, THREAD);
|
true, THREAD);
|
||||||
|
|
||||||
if (kls != NULL) {
|
if (kls != NULL) {
|
||||||
current_class()->class_loader_data()->record_dependency(kls);
|
|
||||||
if (log_is_enabled(Debug, class, resolve)) {
|
if (log_is_enabled(Debug, class, resolve)) {
|
||||||
Verifier::trace_class_resolution(kls, current_class());
|
Verifier::trace_class_resolution(kls, current_class());
|
||||||
}
|
}
|
||||||
|
@ -504,10 +504,6 @@ Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make this class loader depend upon the class loader owning the class reference
|
|
||||||
ClassLoaderData* this_key = this_cp->pool_holder()->class_loader_data();
|
|
||||||
this_key->record_dependency(k);
|
|
||||||
|
|
||||||
// logging for class+resolve.
|
// logging for class+resolve.
|
||||||
if (log_is_enabled(Debug, class, resolve)){
|
if (log_is_enabled(Debug, class, resolve)){
|
||||||
trace_class_resolution(this_cp, k);
|
trace_class_resolution(this_cp, k);
|
||||||
|
@ -872,7 +872,6 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
|
|||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
oop mirror = JNIHandles::resolve_non_null(result);
|
oop mirror = JNIHandles::resolve_non_null(result);
|
||||||
Klass* to_class = java_lang_Class::as_Klass(mirror);
|
Klass* to_class = java_lang_Class::as_Klass(mirror);
|
||||||
ClassLoaderData::class_loader_data(h_loader())->record_dependency(to_class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_is_enabled(Debug, class, resolve) && result != NULL) {
|
if (log_is_enabled(Debug, class, resolve) && result != NULL) {
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 ConstantPoolDependsTest
|
||||||
|
* @bug 8210094
|
||||||
|
* @summary Create ClassLoader dependency from initiating loader to class loader through constant pool reference
|
||||||
|
* @modules java.base/jdk.internal.misc
|
||||||
|
* java.compiler
|
||||||
|
* @library /runtime/testlibrary /test/lib
|
||||||
|
* @build sun.hotspot.WhiteBox
|
||||||
|
* @compile p2/c2.java MyDiffClassLoader.java
|
||||||
|
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||||
|
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||||
|
* @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+PrintSystemDictionaryAtExit -Xlog:class+unload=trace -XX:+WhiteBoxAPI ConstantPoolDependsTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import sun.hotspot.WhiteBox;
|
||||||
|
|
||||||
|
|
||||||
|
public class ConstantPoolDependsTest {
|
||||||
|
public static WhiteBox wb = WhiteBox.getWhiteBox();
|
||||||
|
public static final String MY_TEST = "ConstantPoolDependsTest$c1c";
|
||||||
|
|
||||||
|
public static class c1c {
|
||||||
|
private void test() throws Exception {
|
||||||
|
// ConstantPool.klass_at_impl loads through constant pool and creates dependency
|
||||||
|
p2.c2 c2_obj = new p2.c2();
|
||||||
|
c2_obj.method2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public c1c () throws Exception {
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test() throws Throwable {
|
||||||
|
|
||||||
|
// now use the same loader to load class MyTest
|
||||||
|
Class MyTest_class = new MyDiffClassLoader(MY_TEST).loadClass(MY_TEST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call MyTest to load p2.c2 twice and call p2.c2.method2
|
||||||
|
MyTest_class.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Test FAILED if NoSuchMethodException is thrown");
|
||||||
|
}
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive(MY_TEST), "should not be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive("p2.c2"), "should not be unloaded");
|
||||||
|
// Unless MyTest_class is referenced here, the compiler can unload it.
|
||||||
|
System.out.println("Should not unload anything before here because " + MyTest_class + " is still alive.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Throwable {
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should unload
|
||||||
|
System.gc();
|
||||||
|
System.out.println("Should unload p2.c2 just now");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive(MY_TEST), "should be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive("p2.c2"), "should be unloaded");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 DictionaryDependsTest
|
||||||
|
* @bug 8210094
|
||||||
|
* @summary Create ClassLoader dependency from initiating loader to class loader through reflection
|
||||||
|
* @modules java.base/jdk.internal.misc
|
||||||
|
* java.compiler
|
||||||
|
* @library /runtime/testlibrary /test/lib
|
||||||
|
* @build sun.hotspot.WhiteBox
|
||||||
|
* @compile p2/c2.java MyDiffClassLoader.java
|
||||||
|
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||||
|
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||||
|
* @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -Xlog:class+unload=trace -XX:+WhiteBoxAPI DictionaryDependsTest
|
||||||
|
*/
|
||||||
|
import sun.hotspot.WhiteBox;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class DictionaryDependsTest {
|
||||||
|
public static WhiteBox wb = WhiteBox.getWhiteBox();
|
||||||
|
public static final String MY_TEST = "DictionaryDependsTest$c1r";
|
||||||
|
|
||||||
|
static public class c1r {
|
||||||
|
|
||||||
|
private void test() throws Exception {
|
||||||
|
// forName loads through reflection and doesn't create dependency
|
||||||
|
Class<?> x = Class.forName("p2.c2", true, c1r.class.getClassLoader());
|
||||||
|
Method m = x.getMethod("method2");
|
||||||
|
java.lang.Object t = x.newInstance();
|
||||||
|
m.invoke(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public c1r () throws Exception {
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should unload p2.c2
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should unload p2.c2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test() throws Throwable {
|
||||||
|
|
||||||
|
// now use the same loader to load class MyTest
|
||||||
|
Class MyTest_class = new MyDiffClassLoader(MY_TEST).loadClass(MY_TEST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call MyTest to load p2.c2 twice and call p2.c2.method2
|
||||||
|
MyTest_class.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Not expected NSME");
|
||||||
|
throw new RuntimeException("Not expecting NSME");
|
||||||
|
}
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive(MY_TEST), "should not be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive("p2.c2"), "should not be unloaded");
|
||||||
|
// Unless MyTest_class is referenced here, the compiler can unload it.
|
||||||
|
System.out.println("Should not unload anything before here because " + MyTest_class + " is still alive.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Throwable {
|
||||||
|
DictionaryDependsTest d = new DictionaryDependsTest();
|
||||||
|
d.test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
System.out.println("Should unload MyTest and p2.c2 just now");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive(MY_TEST), "should be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive("p2.c2"), "should be unloaded");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||||
|
|
||||||
|
public class MyDiffClassLoader extends ClassLoader {
|
||||||
|
|
||||||
|
public String loaderName;
|
||||||
|
public static boolean switchClassData = false;
|
||||||
|
|
||||||
|
MyDiffClassLoader(String name) {
|
||||||
|
this.loaderName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class loadClass(String name) throws ClassNotFoundException {
|
||||||
|
if (!name.contains("c1r") &&
|
||||||
|
!name.contains("c1c") &&
|
||||||
|
!name.contains("c1s") &&
|
||||||
|
!name.equals("p2.c2")) {
|
||||||
|
return super.loadClass(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// new loader loads p2.c2
|
||||||
|
if (name.equals("p2.c2") && !loaderName.equals("C2Loader")) {
|
||||||
|
Class<?> c = new MyDiffClassLoader("C2Loader").loadClass(name);
|
||||||
|
switchClassData = true;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = switchClassData ? getNewClassData(name) : getClassData(name);
|
||||||
|
System.out.println("name is " + name);
|
||||||
|
return defineClass(name, data, 0, data.length);
|
||||||
|
}
|
||||||
|
byte[] getClassData(String name) {
|
||||||
|
try {
|
||||||
|
String TempName = name.replaceAll("\\.", "/");
|
||||||
|
String currentDir = System.getProperty("test.classes");
|
||||||
|
String filename = currentDir + File.separator + TempName + ".class";
|
||||||
|
FileInputStream fis = new FileInputStream(filename);
|
||||||
|
byte[] b = new byte[5000];
|
||||||
|
int cnt = fis.read(b, 0, 5000);
|
||||||
|
byte[] c = new byte[cnt];
|
||||||
|
for (int i=0; i<cnt; i++) c[i] = b[i];
|
||||||
|
return c;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return p2.c2 with everything removed
|
||||||
|
byte[] getNewClassData(String name) {
|
||||||
|
return InMemoryJavaCompiler.compile("p2.c2", "package p2; public class c2 { }");
|
||||||
|
}
|
||||||
|
}
|
82
test/hotspot/jtreg/runtime/ClassUnload/SuperDependsTest.java
Normal file
82
test/hotspot/jtreg/runtime/ClassUnload/SuperDependsTest.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 SuperDependsTest
|
||||||
|
* @bug 8210094
|
||||||
|
* @summary Create ClassLoader dependency from initiating loader to class loader through subclassing
|
||||||
|
* @library /test/lib
|
||||||
|
* @modules java.base/jdk.internal.misc
|
||||||
|
* java.compiler
|
||||||
|
* @library /runtime/testlibrary /test/lib
|
||||||
|
* @build sun.hotspot.WhiteBox
|
||||||
|
* @compile p2/c2.java MyDiffClassLoader.java
|
||||||
|
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||||
|
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||||
|
* @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+PrintSystemDictionaryAtExit -Xlog:class+unload=trace -XX:+WhiteBoxAPI SuperDependsTest
|
||||||
|
*/
|
||||||
|
import sun.hotspot.WhiteBox;
|
||||||
|
import p2.*;
|
||||||
|
|
||||||
|
public class SuperDependsTest {
|
||||||
|
public static WhiteBox wb = WhiteBox.getWhiteBox();
|
||||||
|
public static final String MY_TEST = "SuperDependsTest$c1s";
|
||||||
|
|
||||||
|
|
||||||
|
// p2.c2 loads through super class and creates dependency
|
||||||
|
public static class c1s extends p2.c2 {
|
||||||
|
|
||||||
|
private void test() throws Exception {
|
||||||
|
method2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public c1s () throws Exception {
|
||||||
|
test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test() throws Throwable {
|
||||||
|
|
||||||
|
// now use the same loader to load class MyTest
|
||||||
|
Class MyTest_class = new MyDiffClassLoader(MY_TEST).loadClass(MY_TEST);
|
||||||
|
|
||||||
|
// Call MyTest to load p2.c2 twice and call p2.c2.method2
|
||||||
|
MyTest_class.newInstance();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive(MY_TEST), "should not be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(!wb.isClassAlive("p2.c2"), "should not be unloaded");
|
||||||
|
// Unless MyTest_class is referenced here, the compiler can unload it.
|
||||||
|
System.out.println("Should not unload anything before here because " + MyTest_class + " is still alive.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Throwable {
|
||||||
|
SuperDependsTest d = new SuperDependsTest();
|
||||||
|
d.test();
|
||||||
|
ClassUnloadCommon.triggerUnloading(); // should not unload anything
|
||||||
|
System.out.println("Should unload MyTest and p2.c2 just now");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive(MY_TEST), "should be unloaded");
|
||||||
|
ClassUnloadCommon.failIf(wb.isClassAlive("p2.c2"), "should be unloaded");
|
||||||
|
}
|
||||||
|
}
|
28
test/hotspot/jtreg/runtime/ClassUnload/p2/c2.java
Normal file
28
test/hotspot/jtreg/runtime/ClassUnload/p2/c2.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
package p2;
|
||||||
|
|
||||||
|
public class c2 {
|
||||||
|
int i;
|
||||||
|
public void method2() { i = 5; System.out.println("c2 method2 called"); }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user