# HG changeset patch # User stefank # Date 1354007600 -3600 # Node ID 490bb6c0df7c6191a1312cdff9f54865215b4486 # Parent 8427edf5a77bffb6ab0a7d669d4f7caf1b1cab7f 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 diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp --- 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; diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- 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()); diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- 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()); diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp --- 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); diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/memory/iterator.cpp --- 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); } diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/memory/iterator.hpp --- 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 { diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/memory/sharedHeap.cpp --- 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); diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/deoptimization.cpp --- 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; diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/frame.cpp --- 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); } diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/frame.hpp --- 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 diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/thread.cpp --- 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); } } diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/thread.hpp --- 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. diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/vmThread.cpp --- 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); } diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/src/share/vm/runtime/vmThread.hpp --- 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; diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/test/runtime/8003720/Asmator.java --- /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(); + } +} diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/test/runtime/8003720/Test8003720.java --- /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); + } + } +} diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/test/runtime/8003720/Victim.java --- /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(1000000); + Class c = Victim.class; + Test8003720.println("initializing " + c + "#" + System.identityHashCode(c) + " in " + c.getClassLoader()); + } +} diff -r 8427edf5a77b -r 490bb6c0df7c hotspot/test/runtime/8003720/VictimClassLoader.java --- /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; + } +}