8003720: NPG: Method in interpreter stack frame can be deallocated
Summary: Pass down a closure during root scanning to keep the class of the method alive.
Reviewed-by: coleenp, jcoomes
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -52,14 +52,22 @@
PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty));
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(which);
+
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
+ CLDToOopClosure mark_and_push_from_clds(&mark_and_push_closure, true);
CodeBlobToOopClosure mark_and_push_in_blobs(&mark_and_push_closure, /*do_marking=*/ true);
if (_java_thread != NULL)
- _java_thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs);
+ _java_thread->oops_do(
+ &mark_and_push_closure,
+ &mark_and_push_from_clds,
+ &mark_and_push_in_blobs);
if (_vm_thread != NULL)
- _vm_thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs);
+ _vm_thread->oops_do(
+ &mark_and_push_closure,
+ &mark_and_push_from_clds,
+ &mark_and_push_in_blobs);
// Do the real work
cm->follow_marking_stacks();
@@ -89,7 +97,8 @@
{
ResourceMark rm;
CodeBlobToOopClosure each_active_code_blob(&mark_and_push_closure, /*do_marking=*/ true);
- Threads::oops_do(&mark_and_push_closure, &each_active_code_blob);
+ CLDToOopClosure mark_and_push_from_cld(&mark_and_push_closure);
+ Threads::oops_do(&mark_and_push_closure, &mark_and_push_from_cld, &each_active_code_blob);
}
break;
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -495,8 +495,9 @@
ParallelScavengeHeap::ParStrongRootsScope psrs;
Universe::oops_do(mark_and_push_closure());
JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles
+ CLDToOopClosure mark_and_push_from_cld(mark_and_push_closure());
CodeBlobToOopClosure each_active_code_blob(mark_and_push_closure(), /*do_marking=*/ true);
- Threads::oops_do(mark_and_push_closure(), &each_active_code_blob);
+ Threads::oops_do(mark_and_push_closure(), &mark_and_push_from_cld, &each_active_code_blob);
ObjectSynchronizer::oops_do(mark_and_push_closure());
FlatProfiler::oops_do(mark_and_push_closure());
Management::oops_do(mark_and_push_closure());
@@ -584,7 +585,8 @@
// General strong roots.
Universe::oops_do(adjust_root_pointer_closure());
JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
- Threads::oops_do(adjust_root_pointer_closure(), NULL);
+ CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure());
+ Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL);
ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
FlatProfiler::oops_do(adjust_root_pointer_closure());
Management::oops_do(adjust_root_pointer_closure());
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -2436,7 +2436,8 @@
// General strong roots.
Universe::oops_do(adjust_root_pointer_closure());
JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
- Threads::oops_do(adjust_root_pointer_closure(), NULL);
+ CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure());
+ Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL);
ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
FlatProfiler::oops_do(adjust_root_pointer_closure());
Management::oops_do(adjust_root_pointer_closure());
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -65,7 +65,8 @@
case threads:
{
ResourceMark rm;
- Threads::oops_do(&roots_closure, NULL);
+ CLDToOopClosure* cld_closure = NULL; // Not needed. All CLDs are already visited.
+ Threads::oops_do(&roots_closure, cld_closure, NULL);
}
break;
@@ -120,13 +121,14 @@
PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which);
PSScavengeRootsClosure roots_closure(pm);
+ CLDToOopClosure* roots_from_clds = NULL; // Not needed. All CLDs are already visited.
CodeBlobToOopClosure roots_in_blobs(&roots_closure, /*do_marking=*/ true);
if (_java_thread != NULL)
- _java_thread->oops_do(&roots_closure, &roots_in_blobs);
+ _java_thread->oops_do(&roots_closure, roots_from_clds, &roots_in_blobs);
if (_vm_thread != NULL)
- _vm_thread->oops_do(&roots_closure, &roots_in_blobs);
+ _vm_thread->oops_do(&roots_closure, roots_from_clds, &roots_in_blobs);
// Do the real work
pm->drain_stacks(false);
--- a/hotspot/src/share/vm/memory/iterator.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/memory/iterator.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -30,6 +30,10 @@
k->oops_do(_oop_closure);
}
+void CLDToOopClosure::do_cld(ClassLoaderData* cld) {
+ cld->oops_do(_oop_closure, &_klass_closure, _must_claim_cld);
+}
+
void ObjectToOopClosure::do_object(oop obj) {
obj->oop_iterate(_cl);
}
--- a/hotspot/src/share/vm/memory/iterator.hpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/memory/iterator.hpp Tue Nov 27 10:13:20 2012 +0100
@@ -135,6 +135,20 @@
virtual void do_klass(Klass* k);
};
+class CLDToOopClosure {
+ OopClosure* _oop_closure;
+ KlassToOopClosure _klass_closure;
+ bool _must_claim_cld;
+
+ public:
+ CLDToOopClosure(OopClosure* oop_closure, bool must_claim_cld = true) :
+ _oop_closure(oop_closure),
+ _klass_closure(oop_closure),
+ _must_claim_cld(must_claim_cld) {}
+
+ void do_cld(ClassLoaderData* cld);
+};
+
// ObjectClosure is used for iterating through an object space
class ObjectClosure : public Closure {
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -154,10 +154,12 @@
if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do))
JNIHandles::oops_do(roots);
// All threads execute this; the individual threads are task groups.
+ CLDToOopClosure roots_from_clds(roots);
+ CLDToOopClosure* roots_from_clds_p = (is_scavenging ? NULL : &roots_from_clds);
if (ParallelGCThreads > 0) {
- Threads::possibly_parallel_oops_do(roots, code_roots);
+ Threads::possibly_parallel_oops_do(roots, roots_from_clds_p ,code_roots);
} else {
- Threads::oops_do(roots, code_roots);
+ Threads::oops_do(roots, roots_from_clds_p, code_roots);
}
if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do))
ObjectSynchronizer::oops_do(roots);
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -721,7 +721,7 @@
guarantee(false, "wrong number of expression stack elements during deopt");
}
VerifyOopClosure verify;
- iframe->oops_interpreted_do(&verify, &rm, false);
+ iframe->oops_interpreted_do(&verify, NULL, &rm, false);
callee_size_of_parameters = mh->size_of_parameters();
callee_max_locals = mh->max_locals();
is_top_frame = false;
--- a/hotspot/src/share/vm/runtime/frame.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/frame.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -879,7 +879,8 @@
}
-void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache) {
+void frame::oops_interpreted_do(OopClosure* f, CLDToOopClosure* cld_f,
+ const RegisterMap* map, bool query_oop_map_cache) {
assert(is_interpreted_frame(), "Not an interpreted frame");
assert(map != NULL, "map must be set");
Thread *thread = Thread::current();
@@ -906,6 +907,16 @@
}
// process fixed part
+ if (cld_f != NULL) {
+ // The method pointer in the frame might be the only path to the method's
+ // klass, and the klass needs to be kept alive while executing. The GCs
+ // don't trace through method pointers, so typically in similar situations
+ // the mirror or the class loader of the klass are installed as a GC root.
+ // To minimze the overhead of doing that here, we ask the GC to pass down a
+ // closure that knows how to keep klasses alive given a ClassLoaderData.
+ cld_f->do_cld(m->method_holder()->class_loader_data());
+ }
+
#if !defined(PPC) || defined(ZERO)
if (m->is_native()) {
#ifdef CC_INTERP
@@ -1108,7 +1119,7 @@
}
-void frame::oops_do_internal(OopClosure* f, CodeBlobClosure* cf, RegisterMap* map, bool use_interpreter_oop_map_cache) {
+void frame::oops_do_internal(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf, RegisterMap* map, bool use_interpreter_oop_map_cache) {
#ifndef PRODUCT
// simulate GC crash here to dump java thread in error report
if (CrashGCForDumpingJavaThread) {
@@ -1117,7 +1128,7 @@
}
#endif
if (is_interpreted_frame()) {
- oops_interpreted_do(f, map, use_interpreter_oop_map_cache);
+ oops_interpreted_do(f, cld_f, map, use_interpreter_oop_map_cache);
} else if (is_entry_frame()) {
oops_entry_do(f, map);
} else if (CodeCache::contains(pc())) {
@@ -1278,7 +1289,7 @@
}
}
COMPILER2_PRESENT(assert(DerivedPointerTable::is_empty(), "must be empty before verify");)
- oops_do_internal(&VerifyOopClosure::verify_oop, NULL, (RegisterMap*)map, false);
+ oops_do_internal(&VerifyOopClosure::verify_oop, NULL, NULL, (RegisterMap*)map, false);
}
--- a/hotspot/src/share/vm/runtime/frame.hpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/frame.hpp Tue Nov 27 10:13:20 2012 +0100
@@ -413,19 +413,19 @@
// Oops-do's
void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, const RegisterMap* reg_map, OopClosure* f);
- void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true);
+ void oops_interpreted_do(OopClosure* f, CLDToOopClosure* cld_f, const RegisterMap* map, bool query_oop_map_cache = true);
private:
void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f);
// Iteration of oops
- void oops_do_internal(OopClosure* f, CodeBlobClosure* cf, RegisterMap* map, bool use_interpreter_oop_map_cache);
+ void oops_do_internal(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf, RegisterMap* map, bool use_interpreter_oop_map_cache);
void oops_entry_do(OopClosure* f, const RegisterMap* map);
void oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const RegisterMap* map);
int adjust_offset(Method* method, int index); // helper for above fn
public:
// Memory management
- void oops_do(OopClosure* f, CodeBlobClosure* cf, RegisterMap* map) { oops_do_internal(f, cf, map, true); }
+ void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf, RegisterMap* map) { oops_do_internal(f, cld_f, cf, map, true); }
void nmethods_do(CodeBlobClosure* cf);
// RedefineClasses support for finding live interpreted methods on the stack
--- a/hotspot/src/share/vm/runtime/thread.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/thread.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -826,7 +826,7 @@
return false;
}
-void Thread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
+void Thread::oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
active_handles()->oops_do(f);
// Do oop for ThreadShadow
f->do_oop((oop*)&_pending_exception);
@@ -2705,7 +2705,7 @@
}
};
-void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
+void JavaThread::oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
// Verify that the deferred card marks have been flushed.
assert(deferred_card_mark().is_empty(), "Should be empty during GC");
@@ -2713,7 +2713,7 @@
// since there may be more than one thread using each ThreadProfiler.
// Traverse the GCHandles
- Thread::oops_do(f, cf);
+ Thread::oops_do(f, cld_f, cf);
assert( (!has_last_Java_frame() && java_call_counter() == 0) ||
(has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!");
@@ -2741,7 +2741,7 @@
// Traverse the execution stack
for(StackFrameStream fst(this); !fst.is_done(); fst.next()) {
- fst.current()->oops_do(f, cf, fst.register_map());
+ fst.current()->oops_do(f, cld_f, cf, fst.register_map());
}
}
@@ -2875,7 +2875,7 @@
void JavaThread::verify() {
// Verify oops in the thread.
- oops_do(&VerifyOopClosure::verify_oop, NULL);
+ oops_do(&VerifyOopClosure::verify_oop, NULL, NULL);
// Verify the stack frames.
frames_do(frame_verify);
@@ -3125,7 +3125,7 @@
static void oops_print(frame* f, const RegisterMap *map) {
PrintAndVerifyOopClosure print;
f->print_value();
- f->oops_do(&print, NULL, (RegisterMap*)map);
+ f->oops_do(&print, NULL, NULL, (RegisterMap*)map);
}
// Print our all the locations that contain oops and whether they are
@@ -3227,8 +3227,8 @@
#endif
}
-void CompilerThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
- JavaThread::oops_do(f, cf);
+void CompilerThread::oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
+ JavaThread::oops_do(f, cld_f, cf);
if (_scanned_nmethod != NULL && cf != NULL) {
// Safepoints can occur when the sweeper is scanning an nmethod so
// process it here to make sure it isn't unloaded in the middle of
@@ -4201,14 +4201,14 @@
// uses the Threads_lock to gurantee this property. It also makes sure that
// all threads gets blocked when exiting or starting).
-void Threads::oops_do(OopClosure* f, CodeBlobClosure* cf) {
+void Threads::oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
ALL_JAVA_THREADS(p) {
- p->oops_do(f, cf);
+ p->oops_do(f, cld_f, cf);
}
- VMThread::vm_thread()->oops_do(f, cf);
+ VMThread::vm_thread()->oops_do(f, cld_f, cf);
}
-void Threads::possibly_parallel_oops_do(OopClosure* f, CodeBlobClosure* cf) {
+void Threads::possibly_parallel_oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
// Introduce a mechanism allowing parallel threads to claim threads as
// root groups. Overhead should be small enough to use all the time,
// even in sequential code.
@@ -4225,12 +4225,12 @@
int cp = SharedHeap::heap()->strong_roots_parity();
ALL_JAVA_THREADS(p) {
if (p->claim_oops_do(is_par, cp)) {
- p->oops_do(f, cf);
+ p->oops_do(f, cld_f, cf);
}
}
VMThread* vmt = VMThread::vm_thread();
if (vmt->claim_oops_do(is_par, cp)) {
- vmt->oops_do(f, cf);
+ vmt->oops_do(f, cld_f, cf);
}
}
--- a/hotspot/src/share/vm/runtime/thread.hpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/thread.hpp Tue Nov 27 10:13:20 2012 +0100
@@ -480,8 +480,10 @@
// GC support
// Apply "f->do_oop" to all root oops in "this".
+ // Apply "cld_f->do_cld" to CLDs that are otherwise not kept alive.
+ // Used by JavaThread::oops_do.
// Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames
- virtual void oops_do(OopClosure* f, CodeBlobClosure* cf);
+ virtual void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
// Handles the parallel case for the method below.
private:
@@ -1405,7 +1407,7 @@
void frames_do(void f(frame*, const RegisterMap*));
// Memory operations
- void oops_do(OopClosure* f, CodeBlobClosure* cf);
+ void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
// Sweeper operations
void nmethods_do(CodeBlobClosure* cf);
@@ -1825,7 +1827,7 @@
// GC support
// Apply "f->do_oop" to all root oops in "this".
// Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames
- void oops_do(OopClosure* f, CodeBlobClosure* cf);
+ void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
#ifndef PRODUCT
private:
@@ -1892,9 +1894,9 @@
// Apply "f->do_oop" to all root oops in all threads.
// This version may only be called by sequential code.
- static void oops_do(OopClosure* f, CodeBlobClosure* cf);
+ static void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
// This version may be called by sequential or parallel code.
- static void possibly_parallel_oops_do(OopClosure* f, CodeBlobClosure* cf);
+ static void possibly_parallel_oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
// This creates a list of GCTasks, one per thread.
static void create_thread_roots_tasks(GCTaskQueue* q);
// This creates a list of GCTasks, one per thread, for marking objects.
--- a/hotspot/src/share/vm/runtime/vmThread.cpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/vmThread.cpp Tue Nov 27 10:13:20 2012 +0100
@@ -668,8 +668,8 @@
}
-void VMThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
- Thread::oops_do(f, cf);
+void VMThread::oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf) {
+ Thread::oops_do(f, cld_f, cf);
_vm_queue->oops_do(f);
}
@@ -701,5 +701,5 @@
#endif
void VMThread::verify() {
- oops_do(&VerifyOopClosure::verify_oop, NULL);
+ oops_do(&VerifyOopClosure::verify_oop, NULL, NULL);
}
--- a/hotspot/src/share/vm/runtime/vmThread.hpp Mon Nov 26 12:31:03 2012 -0500
+++ b/hotspot/src/share/vm/runtime/vmThread.hpp Tue Nov 27 10:13:20 2012 +0100
@@ -137,7 +137,7 @@
static VMThread* vm_thread() { return _vm_thread; }
// GC support
- void oops_do(OopClosure* f, CodeBlobClosure* cf);
+ void oops_do(OopClosure* f, CLDToOopClosure* cld_f, CodeBlobClosure* cf);
// Debugging
void print_on(outputStream* st) const;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/8003720/Asmator.java Tue Nov 27 10:13:20 2012 +0100
@@ -0,0 +1,31 @@
+import com.sun.xml.internal.ws.org.objectweb.asm.*;
+
+class Asmator {
+ static byte[] fixup(byte[] buf) throws java.io.IOException {
+ ClassReader cr = new ClassReader(buf);
+ ClassWriter cw = new ClassWriter(0) {
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ MethodVisitor mv = super.visitMethod(access,
+ name,
+ desc,
+ signature,
+ exceptions);
+ if (mv == null) return null;
+ if (name.equals("callme")) {
+ // make receiver go dead!
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ mv.visitVarInsn(Opcodes.ASTORE, 0);
+ }
+ return mv;
+ }
+ };
+ cr.accept(cw, 0);
+ return cw.toByteArray();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/8003720/Test8003720.java Tue Nov 27 10:13:20 2012 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, 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
+ * @bug 8003720
+ * @summary Method in interpreter stack frame can be deallocated
+ * @compile -XDignore.symbol.file Victim.java
+ * @run main/othervm -Xverify:all -Xint Test8003720
+ */
+
+// Attempts to make the JVM unload a class while still executing one of its methods.
+public class Test8003720 {
+ final static String VICTIM_CLASS_NAME = "Victim";
+ final static boolean QUIET = true;
+ final static long DURATION = 30000;
+
+ public interface CallMe { void callme(); }
+
+ public static void main(String... av) throws Throwable {
+ newVictimClassLoader();
+ System.gc();
+
+ newVictimClass();
+ System.gc();
+
+ newVictimInstance();
+ System.gc();
+
+ ((CallMe)newVictimInstance()).callme();
+ }
+
+ public static Object newVictimInstance() throws Throwable {
+ return newVictimClass().newInstance();
+ }
+
+ public static Class<?> newVictimClass() throws Throwable {
+ return Class.forName(VICTIM_CLASS_NAME, true, new VictimClassLoader());
+ }
+
+ public static ClassLoader newVictimClassLoader() throws Throwable {
+ return new VictimClassLoader();
+ }
+
+ public static void println(String line) {
+ if (!QUIET) {
+ System.out.println(line);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/8003720/Victim.java Tue Nov 27 10:13:20 2012 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+public class Victim implements Test8003720.CallMe {
+ public void callme() {
+ // note: Victim.this is dead here
+ Test8003720.println("executing in loader=" + Victim.class.getClassLoader());
+
+ long now = System.currentTimeMillis();
+
+ while ((System.currentTimeMillis() - now) < Test8003720.DURATION) {
+ long count = VictimClassLoader.counter++;
+ if (count % 1000000 == 0) System.gc();
+ if (count % 16180000 == 0) blurb();
+ new Object[1].clone();
+ }
+ }
+ static void blurb() {
+ Test8003720.println("count=" + VictimClassLoader.counter);
+ }
+ static {
+ blather();
+ }
+ static void blather() {
+ new java.util.ArrayList<Object>(1000000);
+ Class<Victim> c = Victim.class;
+ Test8003720.println("initializing " + c + "#" + System.identityHashCode(c) + " in " + c.getClassLoader());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/8003720/VictimClassLoader.java Tue Nov 27 10:13:20 2012 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+public class VictimClassLoader extends ClassLoader {
+ public static long counter = 0;
+
+ private int which = (int) ++counter;
+
+ protected VictimClassLoader() {
+ super(VictimClassLoader.class.getClassLoader());
+ }
+
+ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ Class c;
+ if (!name.endsWith("Victim")) {
+ c = super.loadClass(name, resolve);
+ return c;
+ }
+
+ c = findLoadedClass(name);
+ if (c != null) {
+ return c;
+ }
+
+ byte[] buf = readClassFile(name);
+ c = defineClass(name, buf, 0, buf.length);
+ resolveClass(c);
+
+ if (c.getClassLoader() != this) {
+ throw new AssertionError();
+ }
+
+ Test8003720.println("loaded " + c + "#" + System.identityHashCode(c) + " in " + c.getClassLoader());
+ return c;
+ }
+
+ static byte[] readClassFile(String name) {
+ try {
+ String rname = name.substring(name.lastIndexOf('.') + 1) + ".class";
+ java.net.URL url = VictimClassLoader.class.getResource(rname);
+ Test8003720.println("found " + rname + " = " + url);
+
+ java.net.URLConnection connection = url.openConnection();
+ int contentLength = connection.getContentLength();
+ byte[] buf = readFully(connection.getInputStream(), contentLength);
+
+ return Asmator.fixup(buf);
+ } catch (java.io.IOException ex) {
+ throw new Error(ex);
+ }
+ }
+
+ static byte[] readFully(java.io.InputStream in, int len) throws java.io.IOException {
+ // Warning here:
+ return sun.misc.IOUtils.readFully(in, len, true);
+ }
+
+ public void finalize() {
+ Test8003720.println("Goodbye from " + this);
+ }
+
+ public String toString() {
+ return "VictimClassLoader#" + which;
+ }
+}