6766644: Redefinition of compiled method fails with assertion "Can not load classes with the Compiler thread"
authorkamg
Wed, 02 Feb 2011 14:38:01 -0500
changeset 8110 c992c8d52344
parent 8109 26c288ddbec3
child 8112 77eb272084d9
6766644: Redefinition of compiled method fails with assertion "Can not load classes with the Compiler thread" Summary: Defer posting events from the compiler thread: use service thread Reviewed-by: coleenp, dholmes, never, dcubed
hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/LowMemoryDetectorThread.java
hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ServiceThread.java
hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java
hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java
hotspot/src/share/vm/code/nmethod.cpp
hotspot/src/share/vm/code/nmethod.hpp
hotspot/src/share/vm/prims/jvmtiExport.cpp
hotspot/src/share/vm/prims/jvmtiExport.hpp
hotspot/src/share/vm/prims/jvmtiImpl.cpp
hotspot/src/share/vm/prims/jvmtiImpl.hpp
hotspot/src/share/vm/runtime/fprofiler.hpp
hotspot/src/share/vm/runtime/mutexLocker.cpp
hotspot/src/share/vm/runtime/mutexLocker.hpp
hotspot/src/share/vm/runtime/serviceThread.cpp
hotspot/src/share/vm/runtime/serviceThread.hpp
hotspot/src/share/vm/runtime/thread.hpp
hotspot/src/share/vm/runtime/vmStructs.cpp
hotspot/src/share/vm/services/attachListener.hpp
hotspot/src/share/vm/services/lowMemoryDetector.cpp
hotspot/src/share/vm/services/lowMemoryDetector.hpp
hotspot/src/share/vm/services/management.cpp
hotspot/src/share/vm/utilities/macros.hpp
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/LowMemoryDetectorThread.java	Wed Feb 02 18:38:40 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-package sun.jvm.hotspot.runtime;
-
-import java.io.*;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.types.*;
-
-public class LowMemoryDetectorThread extends JavaThread {
-  public LowMemoryDetectorThread(Address addr) {
-    super(addr);
-  }
-
-  public boolean isJavaThread() { return false; }
-  public boolean isHiddenFromExternalView() { return true; }
-  public boolean isLowMemoryDetectorThread() { return true; }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ServiceThread.java	Wed Feb 02 14:38:01 2011 -0500
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2003, 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.jvm.hotspot.runtime;
+
+import java.io.*;
+
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.types.*;
+
+public class ServiceThread extends JavaThread {
+  public ServiceThread(Address addr) {
+    super(addr);
+  }
+
+  public boolean isJavaThread() { return false; }
+  public boolean isHiddenFromExternalView() { return true; }
+  public boolean isServiceThread() { return true; }
+
+}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Thread.java	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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
@@ -111,7 +111,7 @@
   public boolean   isJvmtiAgentThread()        { return false; }
   public boolean   isWatcherThread()           { return false; }
   public boolean   isConcurrentMarkSweepThread() { return false; }
-  public boolean   isLowMemoryDetectorThread() { return false; }
+  public boolean   isServiceThread()           { return false; }
 
   /** Memory operations */
   public void oopsDo(AddressVisitor oopVisitor) {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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
@@ -107,14 +107,14 @@
         // for now, use JavaThread itself. fix it later with appropriate class if needed
         virtualConstructor.addMapping("SurrogateLockerThread", JavaThread.class);
         virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
-        virtualConstructor.addMapping("LowMemoryDetectorThread", LowMemoryDetectorThread.class);
+        virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
     }
 
     public Threads() {
     }
 
     /** NOTE: this returns objects of type JavaThread, CompilerThread,
-      JvmtiAgentThread, and LowMemoryDetectorThread.
+      JvmtiAgentThread, and ServiceThread.
       The latter four are subclasses of the former. Most operations
       (fetching the top frame, etc.) are only allowed to be performed on
       a "pure" JavaThread. For this reason, {@link
@@ -143,7 +143,7 @@
             return thread;
         } catch (Exception e) {
             throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr +
-            " (expected type JavaThread, CompilerThread, LowMemoryDetectorThread, JvmtiAgentThread, or SurrogateLockerThread)", e);
+            " (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread, or SurrogateLockerThread)", e);
         }
     }
 
--- a/hotspot/src/share/vm/code/nmethod.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/code/nmethod.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -34,6 +34,7 @@
 #include "interpreter/bytecode.hpp"
 #include "oops/methodDataOop.hpp"
 #include "prims/jvmtiRedefineClassesTrace.hpp"
+#include "prims/jvmtiImpl.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/sweeper.hpp"
 #include "utilities/dtrace.hpp"
@@ -1533,7 +1534,10 @@
   }
 
   if (JvmtiExport::should_post_compiled_method_load()) {
-    JvmtiExport::post_compiled_method_load(this);
+    // Let the Service thread (which is a real Java thread) post the event
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    JvmtiDeferredEventQueue::enqueue(
+      JvmtiDeferredEvent::compiled_method_load_event(this));
   }
 }
 
@@ -1566,8 +1570,17 @@
   // ref will have been cleared.
   if (_jmethod_id != NULL && JvmtiExport::should_post_compiled_method_unload()) {
     assert(!unload_reported(), "already unloaded");
-    HandleMark hm;
-    JvmtiExport::post_compiled_method_unload(_jmethod_id, insts_begin());
+    JvmtiDeferredEvent event =
+      JvmtiDeferredEvent::compiled_method_unload_event(
+          _jmethod_id, insts_begin());
+    if (SafepointSynchronize::is_at_safepoint()) {
+      // Don't want to take the queueing lock. Add it as pending and
+      // it will get enqueued later.
+      JvmtiDeferredEventQueue::add_pending_event(event);
+    } else {
+      MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+      JvmtiDeferredEventQueue::enqueue(event);
+    }
   }
 
   // The JVMTI CompiledMethodUnload event can be enabled or disabled at
--- a/hotspot/src/share/vm/code/nmethod.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/code/nmethod.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2011, 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
@@ -696,10 +696,11 @@
 class nmethodLocker : public StackObj {
   nmethod* _nm;
 
+ public:
+
   static void lock_nmethod(nmethod* nm);   // note: nm can be NULL
   static void unlock_nmethod(nmethod* nm); // (ditto)
 
- public:
   nmethodLocker(address pc); // derive nm from pc
   nmethodLocker(nmethod *nm) { _nm = nm; lock_nmethod(_nm); }
   nmethodLocker() { _nm = NULL; }
--- a/hotspot/src/share/vm/prims/jvmtiExport.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -750,15 +750,12 @@
 // pending CompiledMethodUnload support
 //
 
-bool JvmtiExport::_have_pending_compiled_method_unload_events;
-GrowableArray<jmethodID>* JvmtiExport::_pending_compiled_method_unload_method_ids;
-GrowableArray<const void *>* JvmtiExport::_pending_compiled_method_unload_code_begins;
-JavaThread* JvmtiExport::_current_poster;
-
-void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethodID method, const void *code_begin) {
+void JvmtiExport::post_compiled_method_unload(
+       jmethodID method, const void *code_begin) {
+  JavaThread* thread = JavaThread::current();
   EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
                  ("JVMTI [%s] method compile unload event triggered",
-                  JvmtiTrace::safe_get_thread_name(self)));
+                  JvmtiTrace::safe_get_thread_name(thread)));
 
   // post the event for each environment that has this event enabled.
   JvmtiEnvIterator it;
@@ -767,12 +764,12 @@
 
       EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
                 ("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT,
-                 JvmtiTrace::safe_get_thread_name(self), method));
+                 JvmtiTrace::safe_get_thread_name(thread), method));
 
-      ResourceMark rm(self);
+      ResourceMark rm(thread);
 
-      JvmtiEventMark jem(self);
-      JvmtiJavaThreadEventTransition jet(self);
+      JvmtiEventMark jem(thread);
+      JvmtiJavaThreadEventTransition jet(thread);
       jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload;
       if (callback != NULL) {
         (*callback)(env->jvmti_external(), method, code_begin);
@@ -781,90 +778,6 @@
   }
 }
 
-// post any pending CompiledMethodUnload events
-
-void JvmtiExport::post_pending_compiled_method_unload_events() {
-  JavaThread* self = JavaThread::current();
-  assert(!self->owns_locks(), "can't hold locks");
-
-  // Indicates if this is the first activiation of this function.
-  // In theory the profiler's callback could call back into VM and provoke
-  // another CompiledMethodLoad event to be posted from this thread. As the
-  // stack rewinds we need to ensure that the original activation does the
-  // completion and notifies any waiters.
-  bool first_activation = false;
-
-  // the jmethodID (may not be valid) to be used for a single event
-  jmethodID method;
-  const void *code_begin;
-
-  // grab the monitor and check if another thread is already posting
-  // events. If there is another thread posting events then we wait
-  // until it completes. (In theory we could check the pending events to
-  // see if any of the addresses overlap with the event that we want to
-  // post but as it will happen so rarely we just block any thread waiting
-  // to post a CompiledMethodLoad or DynamicCodeGenerated event until all
-  // pending CompiledMethodUnload events have been posted).
-  //
-  // If another thread isn't posting we examine the list of pending jmethodIDs.
-  // If the list is empty then we are done. If it's not empty then this thread
-  // (self) becomes the pending event poster and we remove the top (last)
-  // event from the list. Note that this means we remove the newest event first
-  // but as they are all CompiledMethodUnload events the order doesn't matter.
-  // Once we have removed a jmethodID then we exit the monitor. Any other thread
-  // wanting to post a CompiledMethodLoad or DynamicCodeGenerated event will
-  // be forced to wait on the monitor.
-  {
-    MutexLocker mu(JvmtiPendingEvent_lock);
-    if (_current_poster != self) {
-      while (_current_poster != NULL) {
-        JvmtiPendingEvent_lock->wait();
-      }
-    }
-    if ((_pending_compiled_method_unload_method_ids == NULL) ||
-        (_pending_compiled_method_unload_method_ids->length() == 0)) {
-      return;
-    }
-    if (_current_poster == NULL) {
-      _current_poster = self;
-      first_activation = true;
-    } else {
-      // re-entrant
-      guarantee(_current_poster == self, "checking");
-    }
-    method = _pending_compiled_method_unload_method_ids->pop();
-    code_begin = _pending_compiled_method_unload_code_begins->pop();
-  }
-
-  // This thread is the pending event poster so it first posts the CompiledMethodUnload
-  // event for the jmethodID that has been removed from the list. Once posted it
-  // re-grabs the monitor and checks the list again. If the list is empty then and this
-  // is the first activation of the function then we reset the _have_pending_events
-  // flag, cleanup _current_poster to indicate that no thread is now servicing the
-  // pending events list, and finally notify any thread that might be waiting.
-  for (;;) {
-    post_compiled_method_unload_internal(self, method, code_begin);
-
-    // event posted, now re-grab monitor and get the next event
-    // If there's no next event then we are done. If this is the first
-    // activiation of this function by this thread notify any waiters
-    // so that they can post.
-    {
-      MutexLocker ml(JvmtiPendingEvent_lock);
-      if (_pending_compiled_method_unload_method_ids->length() == 0) {
-        if (first_activation) {
-          _have_pending_compiled_method_unload_events = false;
-          _current_poster = NULL;
-          JvmtiPendingEvent_lock->notify_all();
-        }
-        return;
-      }
-      method = _pending_compiled_method_unload_method_ids->pop();
-      code_begin = _pending_compiled_method_unload_code_begins->pop();
-    }
-  }
-}
-
 ///////////////////////////////////////////////////////////////
 //
 // JvmtiExport
@@ -1830,16 +1743,7 @@
 }
 
 void JvmtiExport::post_compiled_method_load(nmethod *nm) {
-  // If there are pending CompiledMethodUnload events then these are
-  // posted before this CompiledMethodLoad event. We "lock" the nmethod and
-  // maintain a handle to the methodOop to ensure that the nmethod isn't
-  // flushed or unloaded while posting the events.
   JavaThread* thread = JavaThread::current();
-  if (have_pending_compiled_method_unload_events()) {
-    methodHandle mh(thread, nm->method());
-    nmethodLocker nml(nm);
-    post_pending_compiled_method_unload_events();
-  }
 
   EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
                  ("JVMTI [%s] method compile load event triggered",
@@ -1854,8 +1758,8 @@
                 JvmtiTrace::safe_get_thread_name(thread),
                 (nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(),
                 (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string()));
-
       ResourceMark rm(thread);
+      HandleMark hm(thread);
 
       // Add inlining information
       jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm);
@@ -1899,28 +1803,6 @@
   }
 }
 
-// used at a safepoint to post a CompiledMethodUnload event
-void JvmtiExport::post_compiled_method_unload(jmethodID mid, const void *code_begin) {
-  if (SafepointSynchronize::is_at_safepoint()) {
-    // Class unloading can cause nmethod unloading which is reported
-    // by the VMThread.  These must be batched to be processed later.
-    if (_pending_compiled_method_unload_method_ids == NULL) {
-      // create list lazily
-      _pending_compiled_method_unload_method_ids = new (ResourceObj::C_HEAP) GrowableArray<jmethodID>(10,true);
-      _pending_compiled_method_unload_code_begins = new (ResourceObj::C_HEAP) GrowableArray<const void *>(10,true);
-    }
-    _pending_compiled_method_unload_method_ids->append(mid);
-    _pending_compiled_method_unload_code_begins->append(code_begin);
-    _have_pending_compiled_method_unload_events = true;
-  } else {
-    // Unloading caused by the sweeper can be reported synchronously.
-    if (have_pending_compiled_method_unload_events()) {
-      post_pending_compiled_method_unload_events();
-    }
-    post_compiled_method_unload_internal(JavaThread::current(), mid, code_begin);
-  }
-}
-
 void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) {
   JavaThread* thread = JavaThread::current();
   EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
@@ -1953,9 +1835,9 @@
     return;
   }
 
-  if (have_pending_compiled_method_unload_events()) {
-    post_pending_compiled_method_unload_events();
-  }
+  // Blocks until everything now in the queue has been posted
+  JvmtiDeferredEventQueue::flush_queue(Thread::current());
+
   post_dynamic_code_generated_internal(name, code_begin, code_end);
 }
 
--- a/hotspot/src/share/vm/prims/jvmtiExport.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2011, 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
@@ -141,25 +141,6 @@
 
 
  private:
-  // CompiledMethodUnload events are reported from the VM thread so they
-  // are collected in lists (of jmethodID/addresses) and the events are posted later
-  // from threads posting CompieldMethodLoad or DynamicCodeGenerated events.
-  static bool _have_pending_compiled_method_unload_events;
-  static GrowableArray<jmethodID>* _pending_compiled_method_unload_method_ids;
-  static GrowableArray<const void *>* _pending_compiled_method_unload_code_begins;
-  static JavaThread* _current_poster;
-
-  // tests if there are CompiledMethodUnload events pending
-  inline static bool have_pending_compiled_method_unload_events() {
-    return _have_pending_compiled_method_unload_events;
-  }
-
-  // posts any pending CompiledMethodUnload events.
-  static void post_pending_compiled_method_unload_events();
-
-  // Perform the actual notification to interested JvmtiEnvs.
-  static void post_compiled_method_unload_internal(JavaThread* self, jmethodID mid, const void* code_begin);
-
   // posts a DynamicCodeGenerated event (internal/private implementation).
   // The public post_dynamic_code_generated* functions make use of the
   // internal implementation.
@@ -256,7 +237,7 @@
   // single stepping management methods
   static void at_single_stepping_point(JavaThread *thread, methodOop method, address location) KERNEL_RETURN;
   static void expose_single_stepping(JavaThread *thread) KERNEL_RETURN;
-  static bool hide_single_stepping(JavaThread *thread) KERNEL_RETURN_(return false;);
+  static bool hide_single_stepping(JavaThread *thread) KERNEL_RETURN_(false);
 
   // Methods that notify the debugger that something interesting has happened in the VM.
   static void post_vm_start              ();
@@ -271,20 +252,20 @@
 
   static oop jni_GetField_probe          (JavaThread *thread, jobject jobj,
     oop obj, klassOop klass, jfieldID fieldID, bool is_static)
-    KERNEL_RETURN_(return NULL;);
+    KERNEL_RETURN_(NULL);
   static oop jni_GetField_probe_nh       (JavaThread *thread, jobject jobj,
     oop obj, klassOop klass, jfieldID fieldID, bool is_static)
-    KERNEL_RETURN_(return NULL;);
+    KERNEL_RETURN_(NULL);
   static void post_field_access_by_jni   (JavaThread *thread, oop obj,
     klassOop klass, jfieldID fieldID, bool is_static) KERNEL_RETURN;
   static void post_field_access          (JavaThread *thread, methodOop method,
     address location, KlassHandle field_klass, Handle object, jfieldID field) KERNEL_RETURN;
   static oop jni_SetField_probe          (JavaThread *thread, jobject jobj,
     oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type,
-    jvalue *value) KERNEL_RETURN_(return NULL;);
+    jvalue *value) KERNEL_RETURN_(NULL);
   static oop jni_SetField_probe_nh       (JavaThread *thread, jobject jobj,
     oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type,
-    jvalue *value) KERNEL_RETURN_(return NULL;);
+    jvalue *value) KERNEL_RETURN_(NULL);
   static void post_field_modification_by_jni(JavaThread *thread, oop obj,
     klassOop klass, jfieldID fieldID, bool is_static, char sig_type,
     jvalue *value);
--- a/hotspot/src/share/vm/prims/jvmtiImpl.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiImpl.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -32,11 +32,13 @@
 #include "prims/jvmtiEventController.inline.hpp"
 #include "prims/jvmtiImpl.hpp"
 #include "prims/jvmtiRedefineClasses.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/handles.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/serviceThread.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframe_hp.hpp"
@@ -910,3 +912,207 @@
   tty->print_cr("]");
 #endif
 }
+
+#ifndef KERNEL
+
+JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_load_event(
+    nmethod* nm) {
+  JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_LOAD);
+  event.set_compiled_method_load(nm);
+  nmethodLocker::lock_nmethod(nm); // will be unlocked when posted
+  return event;
+}
+
+JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_unload_event(
+    jmethodID id, const void* code) {
+  JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_UNLOAD);
+  event.set_compiled_method_unload(id, code);
+  return event;
+}
+
+void JvmtiDeferredEvent::post() {
+  switch(_type) {
+    case TYPE_COMPILED_METHOD_LOAD:
+      JvmtiExport::post_compiled_method_load(compiled_method_load());
+      nmethodLocker::unlock_nmethod(compiled_method_load());
+      break;
+    case TYPE_COMPILED_METHOD_UNLOAD:
+      JvmtiExport::post_compiled_method_unload(
+        compiled_method_unload_method_id(),
+        compiled_method_unload_code_begin());
+      break;
+    case TYPE_FLUSH:
+      JvmtiDeferredEventQueue::flush_complete(flush_state_addr());
+      break;
+    default:
+      ShouldNotReachHere();
+  }
+}
+
+JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_tail = NULL;
+JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_head = NULL;
+
+volatile JvmtiDeferredEventQueue::QueueNode*
+    JvmtiDeferredEventQueue::_pending_list = NULL;
+
+bool JvmtiDeferredEventQueue::has_events() {
+  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+  return _queue_head != NULL || _pending_list != NULL;
+}
+
+void JvmtiDeferredEventQueue::enqueue(const JvmtiDeferredEvent& event) {
+  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+
+  process_pending_events();
+
+  // Events get added to the end of the queue (and are pulled off the front).
+  QueueNode* node = new QueueNode(event);
+  if (_queue_tail == NULL) {
+    _queue_tail = _queue_head = node;
+  } else {
+    assert(_queue_tail->next() == NULL, "Must be the last element in the list");
+    _queue_tail->set_next(node);
+    _queue_tail = node;
+  }
+
+  Service_lock->notify_all();
+  assert((_queue_head == NULL) == (_queue_tail == NULL),
+         "Inconsistent queue markers");
+}
+
+JvmtiDeferredEvent JvmtiDeferredEventQueue::dequeue() {
+  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+
+  process_pending_events();
+
+  assert(_queue_head != NULL, "Nothing to dequeue");
+
+  if (_queue_head == NULL) {
+    // Just in case this happens in product; it shouldn't but let's not crash
+    return JvmtiDeferredEvent();
+  }
+
+  QueueNode* node = _queue_head;
+  _queue_head = _queue_head->next();
+  if (_queue_head == NULL) {
+    _queue_tail = NULL;
+  }
+
+  assert((_queue_head == NULL) == (_queue_tail == NULL),
+         "Inconsistent queue markers");
+
+  JvmtiDeferredEvent event = node->event();
+  delete node;
+  return event;
+}
+
+void JvmtiDeferredEventQueue::add_pending_event(
+    const JvmtiDeferredEvent& event) {
+
+  QueueNode* node = new QueueNode(event);
+
+  bool success = false;
+  QueueNode* prev_value = (QueueNode*)_pending_list;
+  do {
+    node->set_next(prev_value);
+    prev_value = (QueueNode*)Atomic::cmpxchg_ptr(
+        (void*)node, (volatile void*)&_pending_list, (void*)node->next());
+  } while (prev_value != node->next());
+}
+
+// This method transfers any events that were added by someone NOT holding
+// the lock into the mainline queue.
+void JvmtiDeferredEventQueue::process_pending_events() {
+  assert(Service_lock->owned_by_self(), "Must own Service_lock");
+
+  if (_pending_list != NULL) {
+    QueueNode* head =
+        (QueueNode*)Atomic::xchg_ptr(NULL, (volatile void*)&_pending_list);
+
+    assert((_queue_head == NULL) == (_queue_tail == NULL),
+           "Inconsistent queue markers");
+
+    if (head != NULL) {
+      // Since we've treated the pending list as a stack (with newer
+      // events at the beginning), we need to join the bottom of the stack
+      // with the 'tail' of the queue in order to get the events in the
+      // right order.  We do this by reversing the pending list and appending
+      // it to the queue.
+
+      QueueNode* new_tail = head;
+      QueueNode* new_head = NULL;
+
+      // This reverses the list
+      QueueNode* prev = new_tail;
+      QueueNode* node = new_tail->next();
+      new_tail->set_next(NULL);
+      while (node != NULL) {
+        QueueNode* next = node->next();
+        node->set_next(prev);
+        prev = node;
+        node = next;
+      }
+      new_head = prev;
+
+      // Now append the new list to the queue
+      if (_queue_tail != NULL) {
+        _queue_tail->set_next(new_head);
+      } else { // _queue_head == NULL
+        _queue_head = new_head;
+      }
+      _queue_tail = new_tail;
+    }
+  }
+}
+
+enum {
+  // Random - used for debugging
+  FLUSHING  = 0x50403020,
+  FLUSHED   = 0x09080706
+};
+
+void JvmtiDeferredEventQueue::flush_queue(Thread* thread) {
+
+  volatile int flush_state = FLUSHING;
+
+  JvmtiDeferredEvent flush(JvmtiDeferredEvent::TYPE_FLUSH);
+  flush.set_flush_state_addr((int*)&flush_state);
+
+  if (ServiceThread::is_service_thread(thread)) {
+    // If we are the service thread we have to post all preceding events
+    // Use the flush event as a token to indicate when we can stop
+    JvmtiDeferredEvent event;
+    {
+      MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+      enqueue(flush);
+      event = dequeue();
+    }
+    while (!event.is_flush_event() ||
+           event.flush_state_addr() != &flush_state) {
+      event.post();
+      {
+        MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+        event = dequeue();
+      }
+    }
+  } else {
+    // Wake up the service thread so it will process events.  When it gets
+    // to the flush event it will set 'flush_complete' and notify us.
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+    enqueue(flush);
+    while (flush_state != FLUSHED) {
+      assert(flush_state == FLUSHING || flush_state == FLUSHED,
+             "only valid values for this");
+      Service_lock->wait(Mutex::_no_safepoint_check_flag);
+    }
+  }
+}
+
+void JvmtiDeferredEventQueue::flush_complete(int* state_addr) {
+  assert(state_addr != NULL && *state_addr == FLUSHING, "must be");
+  MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  *state_addr = FLUSHED;
+  Service_lock->notify_all();
+}
+
+#endif // ndef KERNEL
--- a/hotspot/src/share/vm/prims/jvmtiImpl.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiImpl.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2011, 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
@@ -433,6 +433,149 @@
 
 #endif // !JVMTI_KERNEL
 
+/**
+ * When a thread (such as the compiler thread or VM thread) cannot post a
+ * JVMTI event itself because the event needs to be posted from a Java
+ * thread, then it can defer the event to the Service thread for posting.
+ * The information needed to post the event is encapsulated into this class
+ * and then enqueued onto the JvmtiDeferredEventQueue, where the Service
+ * thread will pick it up and post it.
+ *
+ * This is currently only used for posting compiled-method-load and unload
+ * events, which we don't want posted from the compiler thread.
+ */
+class JvmtiDeferredEvent VALUE_OBJ_CLASS_SPEC {
+  friend class JvmtiDeferredEventQueue;
+ private:
+  typedef enum {
+    TYPE_NONE,
+    TYPE_COMPILED_METHOD_LOAD,
+    TYPE_COMPILED_METHOD_UNLOAD,
+    TYPE_FLUSH // pseudo-event used to implement flush_queue()
+  } Type;
+
+  Type _type;
+  union {
+    nmethod* compiled_method_load;
+    struct {
+      jmethodID method_id;
+      const void* code_begin;
+    } compiled_method_unload;
+    int* flush_state_addr;
+  } _event_data;
+
+  JvmtiDeferredEvent(Type t) : _type(t) {}
+
+  void set_compiled_method_load(nmethod* nm) {
+    assert(_type == TYPE_COMPILED_METHOD_LOAD, "must be");
+    _event_data.compiled_method_load = nm;
+  }
+
+  nmethod* compiled_method_load() const {
+    assert(_type == TYPE_COMPILED_METHOD_LOAD, "must be");
+    return _event_data.compiled_method_load;
+  }
+
+  void set_compiled_method_unload(jmethodID id, const void* code) {
+    assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be");
+    _event_data.compiled_method_unload.method_id = id;
+    _event_data.compiled_method_unload.code_begin = code;
+  }
+
+  jmethodID compiled_method_unload_method_id() const {
+    assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be");
+    return _event_data.compiled_method_unload.method_id;
+  }
+
+  const void* compiled_method_unload_code_begin() const {
+    assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be");
+    return _event_data.compiled_method_unload.code_begin;
+  }
+
+  bool is_flush_event() const { return _type == TYPE_FLUSH; }
+
+  int* flush_state_addr() const {
+    assert(is_flush_event(), "must be");
+    return _event_data.flush_state_addr;
+  }
+
+  void set_flush_state_addr(int* flag) {
+    assert(is_flush_event(), "must be");
+    _event_data.flush_state_addr = flag;
+  }
+
+ public:
+
+  JvmtiDeferredEvent() : _type(TYPE_NONE) {}
+
+  // Factory methods
+  static JvmtiDeferredEvent compiled_method_load_event(nmethod* nm)
+    KERNEL_RETURN_(JvmtiDeferredEvent());
+  static JvmtiDeferredEvent compiled_method_unload_event(
+      jmethodID id, const void* code) KERNEL_RETURN_(JvmtiDeferredEvent());
+
+  // Actually posts the event.
+  void post() KERNEL_RETURN;
+};
+
+/**
+ * Events enqueued on this queue wake up the Service thread which dequeues
+ * and posts the events.  The Service_lock is required to be held
+ * when operating on the queue (except for the "pending" events).
+ */
+class JvmtiDeferredEventQueue : AllStatic {
+  friend class JvmtiDeferredEvent;
+ private:
+  class QueueNode : public CHeapObj {
+   private:
+    JvmtiDeferredEvent _event;
+    QueueNode* _next;
+
+   public:
+    QueueNode(const JvmtiDeferredEvent& event)
+      : _event(event), _next(NULL) {}
+
+    const JvmtiDeferredEvent& event() const { return _event; }
+    QueueNode* next() const { return _next; }
+
+    void set_next(QueueNode* next) { _next = next; }
+  };
+
+  static QueueNode* _queue_head;             // Hold Service_lock to access
+  static QueueNode* _queue_tail;             // Hold Service_lock to access
+  static volatile QueueNode* _pending_list;  // Uses CAS for read/update
+
+  // Transfers events from the _pending_list to the _queue.
+  static void process_pending_events() KERNEL_RETURN;
+
+  static void flush_complete(int* flush_state) KERNEL_RETURN;
+
+ public:
+  // Must be holding Service_lock when calling these
+  static bool has_events() KERNEL_RETURN_(false);
+  static void enqueue(const JvmtiDeferredEvent& event) KERNEL_RETURN;
+  static JvmtiDeferredEvent dequeue() KERNEL_RETURN_(JvmtiDeferredEvent());
+
+  // This call blocks until all events enqueued prior to this call
+  // have been posted.  The Service_lock is acquired and waited upon.
+  //
+  // Implemented by creating a "flush" event and placing it in the queue.
+  // When the flush event is "posted" it will call flush_complete(), which
+  // will release the caller.
+  //
+  // Can be called by any thread (maybe even the service thread itself).
+  // Not necessary for the caller to be a JavaThread.
+  static void flush_queue(Thread* current) KERNEL_RETURN;
+
+  // Used to enqueue events without using a lock, for times (such as during
+  // safepoint) when we can't or don't want to lock the Service_lock.
+  //
+  // Events will be held off to the side until there's a call to
+  // dequeue(), enqueue(), or process_pending_events() (all of which require
+  // the holding of the Service_lock), and will be enqueued at that time.
+  static void add_pending_event(const JvmtiDeferredEvent&) KERNEL_RETURN;
+};
+
 // Utility macro that checks for NULL pointers:
 #define NULL_CHECK(X, Y) if ((X) == NULL) { return (Y); }
 
--- a/hotspot/src/share/vm/runtime/fprofiler.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/runtime/fprofiler.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -231,13 +231,13 @@
   static void engage(JavaThread* mainThread, bool fullProfile) KERNEL_RETURN ;
   static void disengage() KERNEL_RETURN ;
   static void print(int unused) KERNEL_RETURN ;
-  static bool is_active() KERNEL_RETURN_(return false;) ;
+  static bool is_active() KERNEL_RETURN_(false) ;
 
   // This is NULL if each thread has its own thread profiler,
   // else this is the single thread profiler used by all threads.
   // In particular it makes a difference during garbage collection,
   // where you only want to traverse each thread profiler once.
-  static ThreadProfiler* get_thread_profiler() KERNEL_RETURN_(return NULL;);
+  static ThreadProfiler* get_thread_profiler() KERNEL_RETURN_(NULL);
 
   // Garbage Collection Support
   static void oops_do(OopClosure* f) KERNEL_RETURN ;
@@ -246,13 +246,13 @@
 
   // Returns the start address for a given pc
   // NULL is returned if the PCRecorder is inactive
-  static address bucket_start_for(address pc) KERNEL_RETURN_(return NULL;);
+  static address bucket_start_for(address pc) KERNEL_RETURN_(NULL);
 
   enum { MillisecsPerTick = 10 };   // ms per profiling ticks
 
   // Returns the number of ticks recorded for the bucket
   // pc belongs to.
-  static int bucket_count_for(address pc) KERNEL_RETURN_(return 0;);
+  static int bucket_count_for(address pc) KERNEL_RETURN_(0);
 
 #ifndef FPROF_KERNEL
 
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -129,7 +129,7 @@
 Monitor* GCTaskManager_lock           = NULL;
 
 Mutex*   Management_lock              = NULL;
-Monitor* LowMemory_lock               = NULL;
+Monitor* Service_lock               = NULL;
 
 #define MAX_NUM_MUTEX 128
 static Monitor * _mutex_array[MAX_NUM_MUTEX];
@@ -203,7 +203,7 @@
 
   def(Patching_lock                , Mutex  , special,     true ); // used for safepointing and code patching.
   def(ObjAllocPost_lock            , Monitor, special,     false);
-  def(LowMemory_lock               , Monitor, special,     true ); // used for low memory detection
+  def(Service_lock                 , Monitor, special,     true ); // used for service thread operations
   def(JmethodIdCreation_lock       , Mutex  , leaf,        true ); // used for creating jmethodIDs.
 
   def(SystemDictionary_lock        , Monitor, leaf,        true ); // lookups done by VM thread
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -131,7 +131,7 @@
 extern Mutex*   HotCardCache_lock;               // protects the hot card cache
 
 extern Mutex*   Management_lock;                 // a lock used to serialize JVM management
-extern Monitor* LowMemory_lock;                  // a lock used for low memory detection
+extern Monitor* Service_lock;                    // a lock used for service thread operation
 
 // A MutexLocker provides mutual exclusion with respect to a given mutex
 // for the scope which contains the locker.  The lock is an OS lock, not
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/runtime/serviceThread.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "runtime/interfaceSupport.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/serviceThread.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "prims/jvmtiImpl.hpp"
+
+ServiceThread* ServiceThread::_instance = NULL;
+
+void ServiceThread::initialize() {
+  EXCEPTION_MARK;
+
+  instanceKlassHandle klass (THREAD,  SystemDictionary::Thread_klass());
+  instanceHandle thread_oop = klass->allocate_instance_handle(CHECK);
+
+  const char* name = JDK_Version::is_gte_jdk17x_version() ?
+      "Service Thread" : "Low Memory Detector";
+
+  Handle string = java_lang_String::create_from_str(name, CHECK);
+
+  // Initialize thread_oop to put it into the system threadGroup
+  Handle thread_group (THREAD, Universe::system_thread_group());
+  JavaValue result(T_VOID);
+  JavaCalls::call_special(&result, thread_oop,
+                          klass,
+                          vmSymbols::object_initializer_name(),
+                          vmSymbols::threadgroup_string_void_signature(),
+                          thread_group,
+                          string,
+                          CHECK);
+
+  {
+    MutexLocker mu(Threads_lock);
+    ServiceThread* thread =  new ServiceThread(&service_thread_entry);
+
+    // At this point it may be possible that no osthread was created for the
+    // JavaThread due to lack of memory. We would have to throw an exception
+    // in that case. However, since this must work and we do not allow
+    // exceptions anyway, check and abort if this fails.
+    if (thread == NULL || thread->osthread() == NULL) {
+      vm_exit_during_initialization("java.lang.OutOfMemoryError",
+                                    "unable to create new native thread");
+    }
+
+    java_lang_Thread::set_thread(thread_oop(), thread);
+    java_lang_Thread::set_priority(thread_oop(), NearMaxPriority);
+    java_lang_Thread::set_daemon(thread_oop());
+    thread->set_threadObj(thread_oop());
+
+    Threads::add(thread);
+    Thread::start(thread);
+
+    _instance = thread;
+  }
+}
+
+void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
+  while (true) {
+    bool sensors_changed = false;
+    bool has_jvmti_events = false;
+    JvmtiDeferredEvent jvmti_event;
+    {
+      // Need state transition ThreadBlockInVM so that this thread
+      // will be handled by safepoint correctly when this thread is
+      // notified at a safepoint.
+
+      // This ThreadBlockInVM object is not also considered to be
+      // suspend-equivalent because ServiceThread is not visible to
+      // external suspension.
+
+      ThreadBlockInVM tbivm(jt);
+
+      MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+      while (!(sensors_changed = LowMemoryDetector::has_pending_requests()) &&
+             !(has_jvmti_events = JvmtiDeferredEventQueue::has_events())) {
+        // wait until one of the sensors has pending requests, or there is a
+        // pending JVMTI event to post
+        Service_lock->wait(Mutex::_no_safepoint_check_flag);
+      }
+
+      if (has_jvmti_events) {
+        jvmti_event = JvmtiDeferredEventQueue::dequeue();
+      }
+    }
+
+    if (has_jvmti_events) {
+      jvmti_event.post();
+    }
+
+    if (sensors_changed) {
+      LowMemoryDetector::process_sensor_changes(jt);
+    }
+  }
+}
+
+bool ServiceThread::is_service_thread(Thread* thread) {
+  return thread == _instance;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/runtime/serviceThread.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ */
+
+#ifndef SHARE_VM_RUNTIME_SERVICETHREAD_HPP
+#define SHARE_VM_RUNTIME_SERVICETHREAD_HPP
+
+#include "runtime/thread.hpp"
+
+// A JavaThread for low memory detection support and JVMTI
+// compiled-method-load events.
+class ServiceThread : public JavaThread {
+  friend class VMStructs;
+ private:
+
+  static ServiceThread* _instance;
+
+  static void service_thread_entry(JavaThread* thread, TRAPS);
+  ServiceThread(ThreadFunction entry_point) : JavaThread(entry_point) {};
+
+ public:
+  static void initialize();
+
+  // Hide this thread from external view.
+  bool is_hidden_from_external_view() const      { return true; }
+
+  // Returns true if the passed thread is the service thread.
+  static bool is_service_thread(Thread* thread);
+};
+
+#endif // SHARE_VM_RUNTIME_SERVICETHREAD_HPP
--- a/hotspot/src/share/vm/runtime/thread.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/runtime/thread.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1680,16 +1680,6 @@
   return cur_sp > low_addr ? cur_sp - low_addr : 0;
 }
 
-// A JavaThread for low memory detection support
-class LowMemoryDetectorThread : public JavaThread {
-  friend class VMStructs;
-public:
-  LowMemoryDetectorThread(ThreadFunction entry_point) : JavaThread(entry_point) {};
-
-  // Hide this thread from external view.
-  bool is_hidden_from_external_view() const      { return true; }
-};
-
 // A thread used for Compilation.
 class CompilerThread : public JavaThread {
   friend class VMStructs;
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -93,6 +93,7 @@
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/perfMemory.hpp"
+#include "runtime/serviceThread.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/virtualspace.hpp"
@@ -1250,7 +1251,7 @@
            declare_type(WatcherThread, Thread)                            \
            declare_type(JavaThread, Thread)                               \
            declare_type(JvmtiAgentThread, JavaThread)                     \
-           declare_type(LowMemoryDetectorThread, JavaThread)              \
+           declare_type(ServiceThread, JavaThread)                        \
   declare_type(CompilerThread, JavaThread)                        \
   declare_toplevel_type(OSThread)                                         \
   declare_toplevel_type(JavaFrameAnchor)                                  \
--- a/hotspot/src/share/vm/services/attachListener.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/services/attachListener.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -59,10 +59,10 @@
   static void detachall() KERNEL_RETURN;
 
   // indicates if the Attach Listener needs to be created at startup
-  static bool init_at_startup() KERNEL_RETURN_(return false;);
+  static bool init_at_startup() KERNEL_RETURN_(false);
 
   // indicates if we have a trigger to start the Attach Listener
-  static bool is_init_trigger() KERNEL_RETURN_(return false;);
+  static bool is_init_trigger() KERNEL_RETURN_(false);
 
 #ifdef SERVICES_KERNEL
   static bool is_attach_supported()             { return false; }
--- a/hotspot/src/share/vm/services/lowMemoryDetector.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/services/lowMemoryDetector.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -34,55 +34,11 @@
 #include "services/lowMemoryDetector.hpp"
 #include "services/management.hpp"
 
-LowMemoryDetectorThread* LowMemoryDetector::_detector_thread = NULL;
 volatile bool LowMemoryDetector::_enabled_for_collected_pools = false;
 volatile jint LowMemoryDetector::_disabled_count = 0;
 
-void LowMemoryDetector::initialize() {
-  EXCEPTION_MARK;
-
-  instanceKlassHandle klass (THREAD,  SystemDictionary::Thread_klass());
-  instanceHandle thread_oop = klass->allocate_instance_handle(CHECK);
-
-  const char thread_name[] = "Low Memory Detector";
-  Handle string = java_lang_String::create_from_str(thread_name, CHECK);
-
-  // Initialize thread_oop to put it into the system threadGroup
-  Handle thread_group (THREAD, Universe::system_thread_group());
-  JavaValue result(T_VOID);
-  JavaCalls::call_special(&result, thread_oop,
-                          klass,
-                          vmSymbols::object_initializer_name(),
-                          vmSymbols::threadgroup_string_void_signature(),
-                          thread_group,
-                          string,
-                          CHECK);
-
-  {
-    MutexLocker mu(Threads_lock);
-    _detector_thread = new LowMemoryDetectorThread(&low_memory_detector_thread_entry);
-
-    // At this point it may be possible that no osthread was created for the
-    // JavaThread due to lack of memory. We would have to throw an exception
-    // in that case. However, since this must work and we do not allow
-    // exceptions anyway, check and abort if this fails.
-    if (_detector_thread == NULL || _detector_thread->osthread() == NULL) {
-      vm_exit_during_initialization("java.lang.OutOfMemoryError",
-                                    "unable to create new native thread");
-    }
-
-    java_lang_Thread::set_thread(thread_oop(), _detector_thread);
-    java_lang_Thread::set_priority(thread_oop(), NearMaxPriority);
-    java_lang_Thread::set_daemon(thread_oop());
-    _detector_thread->set_threadObj(thread_oop());
-
-    Threads::add(_detector_thread);
-    Thread::start(_detector_thread);
-  }
-}
-
 bool LowMemoryDetector::has_pending_requests() {
-  assert(LowMemory_lock->owned_by_self(), "Must own LowMemory_lock");
+  assert(Service_lock->owned_by_self(), "Must own Service_lock");
   bool has_requests = false;
   int num_memory_pools = MemoryService::num_memory_pools();
   for (int i = 0; i < num_memory_pools; i++) {
@@ -100,47 +56,21 @@
   return has_requests;
 }
 
-void LowMemoryDetector::low_memory_detector_thread_entry(JavaThread* jt, TRAPS) {
-  while (true) {
-    bool   sensors_changed = false;
-
-    {
-      // _no_safepoint_check_flag is used here as LowMemory_lock is a
-      // special lock and the VMThread may acquire this lock at safepoint.
-      // Need state transition ThreadBlockInVM so that this thread
-      // will be handled by safepoint correctly when this thread is
-      // notified at a safepoint.
-
-      // This ThreadBlockInVM object is not also considered to be
-      // suspend-equivalent because LowMemoryDetector threads are
-      // not visible to external suspension.
-
-      ThreadBlockInVM tbivm(jt);
+void LowMemoryDetector::process_sensor_changes(TRAPS) {
+  ResourceMark rm(THREAD);
+  HandleMark hm(THREAD);
 
-      MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
-      while (!(sensors_changed = has_pending_requests())) {
-        // wait until one of the sensors has pending requests
-        LowMemory_lock->wait(Mutex::_no_safepoint_check_flag);
-      }
+  // No need to hold Service_lock to call out to Java
+  int num_memory_pools = MemoryService::num_memory_pools();
+  for (int i = 0; i < num_memory_pools; i++) {
+    MemoryPool* pool = MemoryService::get_memory_pool(i);
+    SensorInfo* sensor = pool->usage_sensor();
+    SensorInfo* gc_sensor = pool->gc_usage_sensor();
+    if (sensor != NULL && sensor->has_pending_requests()) {
+      sensor->process_pending_requests(CHECK);
     }
-
-    {
-      ResourceMark rm(THREAD);
-      HandleMark hm(THREAD);
-
-      // No need to hold LowMemory_lock to call out to Java
-      int num_memory_pools = MemoryService::num_memory_pools();
-      for (int i = 0; i < num_memory_pools; i++) {
-        MemoryPool* pool = MemoryService::get_memory_pool(i);
-        SensorInfo* sensor = pool->usage_sensor();
-        SensorInfo* gc_sensor = pool->gc_usage_sensor();
-        if (sensor != NULL && sensor->has_pending_requests()) {
-          sensor->process_pending_requests(CHECK);
-        }
-        if (gc_sensor != NULL && gc_sensor->has_pending_requests()) {
-          gc_sensor->process_pending_requests(CHECK);
-        }
-      }
+    if (gc_sensor != NULL && gc_sensor->has_pending_requests()) {
+      gc_sensor->process_pending_requests(CHECK);
     }
   }
 }
@@ -148,7 +78,7 @@
 // This method could be called from any Java threads
 // and also VMThread.
 void LowMemoryDetector::detect_low_memory() {
-  MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
+  MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
 
   bool has_pending_requests = false;
   int num_memory_pools = MemoryService::num_memory_pools();
@@ -166,7 +96,7 @@
   }
 
   if (has_pending_requests) {
-    LowMemory_lock->notify_all();
+    Service_lock->notify_all();
   }
 }
 
@@ -181,14 +111,14 @@
   }
 
   {
-    MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
 
     MemoryUsage usage = pool->get_memory_usage();
     sensor->set_gauge_sensor_level(usage,
                                    pool->usage_threshold());
     if (sensor->has_pending_requests()) {
       // notify sensor state update
-      LowMemory_lock->notify_all();
+      Service_lock->notify_all();
     }
   }
 }
@@ -203,14 +133,14 @@
   }
 
   {
-    MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
 
     MemoryUsage usage = pool->get_last_collection_usage();
     sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold());
 
     if (sensor->has_pending_requests()) {
       // notify sensor state update
-      LowMemory_lock->notify_all();
+      Service_lock->notify_all();
     }
   }
 }
@@ -384,8 +314,8 @@
   }
 
   {
-    // Holds LowMemory_lock and update the sensor state
-    MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
+    // Holds Service_lock and update the sensor state
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
     _sensor_on = true;
     _sensor_count += count;
     _pending_trigger_count = _pending_trigger_count - count;
@@ -410,8 +340,8 @@
   }
 
   {
-    // Holds LowMemory_lock and update the sensor state
-    MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag);
+    // Holds Service_lock and update the sensor state
+    MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
     _sensor_on = false;
     _pending_clear_count = 0;
     _pending_trigger_count = _pending_trigger_count - count;
--- a/hotspot/src/share/vm/services/lowMemoryDetector.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/services/lowMemoryDetector.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -58,8 +58,8 @@
 //
 // May need to deal with hysteresis effect.
 //
+// Memory detection code runs in the Service thread (serviceThread.hpp).
 
-class LowMemoryDetectorThread;
 class OopClosure;
 class MemoryPool;
 
@@ -211,23 +211,22 @@
 };
 
 class LowMemoryDetector : public AllStatic {
-friend class LowMemoryDetectorDisabler;
+  friend class LowMemoryDetectorDisabler;
+  friend class ServiceThread;
 private:
   // true if any collected heap has low memory detection enabled
   static volatile bool _enabled_for_collected_pools;
   // > 0 if temporary disabed
   static volatile jint _disabled_count;
 
-  static LowMemoryDetectorThread* _detector_thread;
-  static void low_memory_detector_thread_entry(JavaThread* thread, TRAPS);
   static void check_memory_usage();
   static bool has_pending_requests();
   static bool temporary_disabled() { return _disabled_count > 0; }
   static void disable() { Atomic::inc(&_disabled_count); }
   static void enable() { Atomic::dec(&_disabled_count); }
+  static void process_sensor_changes(TRAPS);
 
 public:
-  static void initialize();
   static void detect_low_memory();
   static void detect_low_memory(MemoryPool* pool);
   static void detect_after_gc_memory(MemoryPool* pool);
@@ -275,7 +274,6 @@
       }
     }
   }
-
 };
 
 class LowMemoryDetectorDisabler: public StackObj {
--- a/hotspot/src/share/vm/services/management.cpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/services/management.cpp	Wed Feb 02 14:38:01 2011 -0500
@@ -38,6 +38,7 @@
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniHandles.hpp"
 #include "runtime/os.hpp"
+#include "runtime/serviceThread.hpp"
 #include "services/classLoadingService.hpp"
 #include "services/heapDumper.hpp"
 #include "services/lowMemoryDetector.hpp"
@@ -112,8 +113,8 @@
 }
 
 void Management::initialize(TRAPS) {
-  // Start the low memory detector thread
-  LowMemoryDetector::initialize();
+  // Start the service thread
+  ServiceThread::initialize();
 
   if (ManagementServer) {
     ResourceMark rm(THREAD);
--- a/hotspot/src/share/vm/utilities/macros.hpp	Wed Feb 02 18:38:40 2011 -0500
+++ b/hotspot/src/share/vm/utilities/macros.hpp	Wed Feb 02 14:38:01 2011 -0500
@@ -46,7 +46,7 @@
 #define SERVICES_KERNEL
 
 #define KERNEL_RETURN        {}
-#define KERNEL_RETURN_(code) { code }
+#define KERNEL_RETURN_(code) { return code; }
 
 #else  // KERNEL