mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8003720: NPG: Method in interpreter stack frame can be deallocated
Pass down a closure during root scanning to keep the class of the method alive. Reviewed-by: coleenp, jcoomes
This commit is contained in:
parent
2e2b69e96b
commit
3c2f3a321a
18 changed files with 327 additions and 42 deletions
|
@ -52,14 +52,22 @@ void ThreadRootsMarkingTask::do_it(GCTaskManager* manager, uint which) {
|
|||
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 @@ void MarkFromRootsTask::do_it(GCTaskManager* manager, uint which) {
|
|||
{
|
||||
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;
|
||||
|
||||
|
|
|
@ -495,8 +495,9 @@ void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) {
|
|||
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 @@ void PSMarkSweep::mark_sweep_phase3() {
|
|||
// 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());
|
||||
|
|
|
@ -2436,7 +2436,8 @@ void PSParallelCompact::adjust_roots() {
|
|||
// 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());
|
||||
|
|
|
@ -65,7 +65,8 @@ void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
|
|||
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 @@ void ThreadRootsTask::do_it(GCTaskManager* manager, uint which) {
|
|||
|
||||
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);
|
||||
|
|
|
@ -30,6 +30,10 @@ void KlassToOopClosure::do_klass(Klass* k) {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -135,6 +135,20 @@ class KlassToOopClosure : public KlassClosure {
|
|||
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 {
|
||||
|
|
|
@ -154,10 +154,12 @@ void SharedHeap::process_strong_roots(bool activate_scope,
|
|||
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);
|
||||
|
|
|
@ -721,7 +721,7 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m
|
|||
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;
|
||||
|
|
|
@ -879,7 +879,8 @@ oop* frame::interpreter_callee_receiver_addr(Symbol* signature) {
|
|||
}
|
||||
|
||||
|
||||
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 @@ void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool quer
|
|||
}
|
||||
|
||||
// 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_entry_do(OopClosure* f, const RegisterMap* map) {
|
|||
}
|
||||
|
||||
|
||||
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 @@ void frame::oops_do_internal(OopClosure* f, CodeBlobClosure* cf, RegisterMap* ma
|
|||
}
|
||||
#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 @@ void frame::verify(const RegisterMap* map) {
|
|||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -413,19 +413,19 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
|||
|
||||
// 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
|
||||
|
|
|
@ -826,7 +826,7 @@ bool Thread::claim_oops_do_par_case(int strong_roots_parity) {
|
|||
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 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
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 @@ void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
|
|||
// 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 @@ void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
|
|||
|
||||
// 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 @@ static void frame_verify(frame* f, const RegisterMap *map) { f->verify(map); }
|
|||
|
||||
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 @@ class PrintAndVerifyOopClosure: public OopClosure {
|
|||
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 @@ CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters)
|
|||
#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 @@ bool Threads::includes(JavaThread* p) {
|
|||
// 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 @@ void Threads::possibly_parallel_oops_do(OopClosure* f, CodeBlobClosure* cf) {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -480,8 +480,10 @@ class Thread: public ThreadShadow {
|
|||
|
||||
// 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 @@ class JavaThread: public Thread {
|
|||
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 @@ class CompilerThread : public JavaThread {
|
|||
// 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 @@ class Threads: AllStatic {
|
|||
|
||||
// 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.
|
||||
|
|
|
@ -668,8 +668,8 @@ void VMThread::execute(VM_Operation* op) {
|
|||
}
|
||||
|
||||
|
||||
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 @@ void VMOperationQueue::verify_queue(int prio) {
|
|||
#endif
|
||||
|
||||
void VMThread::verify() {
|
||||
oops_do(&VerifyOopClosure::verify_oop, NULL);
|
||||
oops_do(&VerifyOopClosure::verify_oop, NULL, NULL);
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ class VMThread: public NamedThread {
|
|||
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;
|
||||
|
|
31
hotspot/test/runtime/8003720/Asmator.java
Normal file
31
hotspot/test/runtime/8003720/Asmator.java
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
71
hotspot/test/runtime/8003720/Test8003720.java
Normal file
71
hotspot/test/runtime/8003720/Test8003720.java
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
50
hotspot/test/runtime/8003720/Victim.java
Normal file
50
hotspot/test/runtime/8003720/Victim.java
Normal file
|
@ -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());
|
||||
}
|
||||
}
|
86
hotspot/test/runtime/8003720/VictimClassLoader.java
Normal file
86
hotspot/test/runtime/8003720/VictimClassLoader.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue