From b39c2e539c2419286ab73d082a4c18035af9b984 Mon Sep 17 00:00:00 2001 From: stefank Date: Tue, 27 Nov 2012 10:13:20 +0100 Subject: [PATCH] 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 --- .../parallelScavenge/pcTasks.cpp | 15 +++- .../parallelScavenge/psMarkSweep.cpp | 6 +- .../parallelScavenge/psParallelCompact.cpp | 3 +- .../parallelScavenge/psTasks.cpp | 8 +- hotspot/src/share/vm/memory/iterator.cpp | 4 + hotspot/src/share/vm/memory/iterator.hpp | 14 +++ hotspot/src/share/vm/memory/sharedHeap.cpp | 6 +- .../src/share/vm/runtime/deoptimization.cpp | 2 +- hotspot/src/share/vm/runtime/frame.cpp | 19 +++- hotspot/src/share/vm/runtime/frame.hpp | 6 +- hotspot/src/share/vm/runtime/thread.cpp | 28 +++--- hotspot/src/share/vm/runtime/thread.hpp | 12 +-- hotspot/src/share/vm/runtime/vmThread.cpp | 6 +- hotspot/src/share/vm/runtime/vmThread.hpp | 2 +- hotspot/test/runtime/8003720/Asmator.java | 31 +++++++ hotspot/test/runtime/8003720/Test8003720.java | 71 +++++++++++++++ hotspot/test/runtime/8003720/Victim.java | 50 +++++++++++ .../runtime/8003720/VictimClassLoader.java | 86 +++++++++++++++++++ 18 files changed, 327 insertions(+), 42 deletions(-) create mode 100644 hotspot/test/runtime/8003720/Asmator.java create mode 100644 hotspot/test/runtime/8003720/Test8003720.java create mode 100644 hotspot/test/runtime/8003720/Victim.java create mode 100644 hotspot/test/runtime/8003720/VictimClassLoader.java diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp index 7b9ff16505..3834722fa7 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp @@ -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; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp index a37555d2c3..63cd376028 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp @@ -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()); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp index fc6d5ef59d..9fbf01169b 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp @@ -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()); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp index 605792671a..ccad48f358 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp @@ -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); diff --git a/hotspot/src/share/vm/memory/iterator.cpp b/hotspot/src/share/vm/memory/iterator.cpp index af57203355..e33a5614d6 100644 --- a/hotspot/src/share/vm/memory/iterator.cpp +++ b/hotspot/src/share/vm/memory/iterator.cpp @@ -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); } diff --git a/hotspot/src/share/vm/memory/iterator.hpp b/hotspot/src/share/vm/memory/iterator.hpp index 825bc23eed..736efdd618 100644 --- a/hotspot/src/share/vm/memory/iterator.hpp +++ b/hotspot/src/share/vm/memory/iterator.hpp @@ -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 { diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp index 773be4c659..be641d304b 100644 --- a/hotspot/src/share/vm/memory/sharedHeap.cpp +++ b/hotspot/src/share/vm/memory/sharedHeap.cpp @@ -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); diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index 01f4dc75fe..c761c0b337 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -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; diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index f9acadd452..1788d3d0de 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -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); } diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp index 8f6d759d6d..6e990dce73 100644 --- a/hotspot/src/share/vm/runtime/frame.hpp +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -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 diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index 653d8e5f82..06bf2081da 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -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); } } diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index 3ccbf487e9..627000c723 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -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. diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp index 470d6b1c44..e427d30361 100644 --- a/hotspot/src/share/vm/runtime/vmThread.cpp +++ b/hotspot/src/share/vm/runtime/vmThread.cpp @@ -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); } diff --git a/hotspot/src/share/vm/runtime/vmThread.hpp b/hotspot/src/share/vm/runtime/vmThread.hpp index 30e45ee265..81832fc594 100644 --- a/hotspot/src/share/vm/runtime/vmThread.hpp +++ b/hotspot/src/share/vm/runtime/vmThread.hpp @@ -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; diff --git a/hotspot/test/runtime/8003720/Asmator.java b/hotspot/test/runtime/8003720/Asmator.java new file mode 100644 index 0000000000..9aad587508 --- /dev/null +++ b/hotspot/test/runtime/8003720/Asmator.java @@ -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(); + } +} diff --git a/hotspot/test/runtime/8003720/Test8003720.java b/hotspot/test/runtime/8003720/Test8003720.java new file mode 100644 index 0000000000..bb0592f64b --- /dev/null +++ b/hotspot/test/runtime/8003720/Test8003720.java @@ -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); + } + } +} diff --git a/hotspot/test/runtime/8003720/Victim.java b/hotspot/test/runtime/8003720/Victim.java new file mode 100644 index 0000000000..c671e60304 --- /dev/null +++ b/hotspot/test/runtime/8003720/Victim.java @@ -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(1000000); + Class c = Victim.class; + Test8003720.println("initializing " + c + "#" + System.identityHashCode(c) + " in " + c.getClassLoader()); + } +} diff --git a/hotspot/test/runtime/8003720/VictimClassLoader.java b/hotspot/test/runtime/8003720/VictimClassLoader.java new file mode 100644 index 0000000000..3505f7832f --- /dev/null +++ b/hotspot/test/runtime/8003720/VictimClassLoader.java @@ -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; + } +} -- GitLab