提交 ffd09be1 编写于 作者: K kamg

6766644: Redefinition of compiled method fails with assertion "Can not load...

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
上级 dd877cb2
/* /*
* Copyright (c) 2003, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,13 +29,13 @@ import java.io.*; ...@@ -29,13 +29,13 @@ import java.io.*;
import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.types.*; import sun.jvm.hotspot.types.*;
public class LowMemoryDetectorThread extends JavaThread { public class ServiceThread extends JavaThread {
public LowMemoryDetectorThread(Address addr) { public ServiceThread(Address addr) {
super(addr); super(addr);
} }
public boolean isJavaThread() { return false; } public boolean isJavaThread() { return false; }
public boolean isHiddenFromExternalView() { return true; } public boolean isHiddenFromExternalView() { return true; }
public boolean isLowMemoryDetectorThread() { return true; } public boolean isServiceThread() { return true; }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -111,7 +111,7 @@ public class Thread extends VMObject { ...@@ -111,7 +111,7 @@ public class Thread extends VMObject {
public boolean isJvmtiAgentThread() { return false; } public boolean isJvmtiAgentThread() { return false; }
public boolean isWatcherThread() { return false; } public boolean isWatcherThread() { return false; }
public boolean isConcurrentMarkSweepThread() { return false; } public boolean isConcurrentMarkSweepThread() { return false; }
public boolean isLowMemoryDetectorThread() { return false; } public boolean isServiceThread() { return false; }
/** Memory operations */ /** Memory operations */
public void oopsDo(AddressVisitor oopVisitor) { public void oopsDo(AddressVisitor oopVisitor) {
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -107,14 +107,14 @@ public class Threads { ...@@ -107,14 +107,14 @@ public class Threads {
// for now, use JavaThread itself. fix it later with appropriate class if needed // for now, use JavaThread itself. fix it later with appropriate class if needed
virtualConstructor.addMapping("SurrogateLockerThread", JavaThread.class); virtualConstructor.addMapping("SurrogateLockerThread", JavaThread.class);
virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
virtualConstructor.addMapping("LowMemoryDetectorThread", LowMemoryDetectorThread.class); virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
} }
public Threads() { public Threads() {
} }
/** NOTE: this returns objects of type JavaThread, CompilerThread, /** NOTE: this returns objects of type JavaThread, CompilerThread,
JvmtiAgentThread, and LowMemoryDetectorThread. JvmtiAgentThread, and ServiceThread.
The latter four are subclasses of the former. Most operations The latter four are subclasses of the former. Most operations
(fetching the top frame, etc.) are only allowed to be performed on (fetching the top frame, etc.) are only allowed to be performed on
a "pure" JavaThread. For this reason, {@link a "pure" JavaThread. For this reason, {@link
...@@ -143,7 +143,7 @@ public class Threads { ...@@ -143,7 +143,7 @@ public class Threads {
return thread; return thread;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + 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);
} }
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "interpreter/bytecode.hpp" #include "interpreter/bytecode.hpp"
#include "oops/methodDataOop.hpp" #include "oops/methodDataOop.hpp"
#include "prims/jvmtiRedefineClassesTrace.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp"
#include "prims/jvmtiImpl.hpp"
#include "runtime/sharedRuntime.hpp" #include "runtime/sharedRuntime.hpp"
#include "runtime/sweeper.hpp" #include "runtime/sweeper.hpp"
#include "utilities/dtrace.hpp" #include "utilities/dtrace.hpp"
...@@ -1533,7 +1534,10 @@ void nmethod::post_compiled_method_load_event() { ...@@ -1533,7 +1534,10 @@ void nmethod::post_compiled_method_load_event() {
} }
if (JvmtiExport::should_post_compiled_method_load()) { 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 @@ void nmethod::post_compiled_method_unload() { ...@@ -1566,8 +1570,17 @@ void nmethod::post_compiled_method_unload() {
// ref will have been cleared. // ref will have been cleared.
if (_jmethod_id != NULL && JvmtiExport::should_post_compiled_method_unload()) { if (_jmethod_id != NULL && JvmtiExport::should_post_compiled_method_unload()) {
assert(!unload_reported(), "already unloaded"); assert(!unload_reported(), "already unloaded");
HandleMark hm; JvmtiDeferredEvent event =
JvmtiExport::post_compiled_method_unload(_jmethod_id, insts_begin()); 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 // The JVMTI CompiledMethodUnload event can be enabled or disabled at
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -696,10 +696,11 @@ public: ...@@ -696,10 +696,11 @@ public:
class nmethodLocker : public StackObj { class nmethodLocker : public StackObj {
nmethod* _nm; nmethod* _nm;
public:
static void lock_nmethod(nmethod* nm); // note: nm can be NULL static void lock_nmethod(nmethod* nm); // note: nm can be NULL
static void unlock_nmethod(nmethod* nm); // (ditto) static void unlock_nmethod(nmethod* nm); // (ditto)
public:
nmethodLocker(address pc); // derive nm from pc nmethodLocker(address pc); // derive nm from pc
nmethodLocker(nmethod *nm) { _nm = nm; lock_nmethod(_nm); } nmethodLocker(nmethod *nm) { _nm = nm; lock_nmethod(_nm); }
nmethodLocker() { _nm = NULL; } nmethodLocker() { _nm = NULL; }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -750,15 +750,12 @@ public: ...@@ -750,15 +750,12 @@ public:
// pending CompiledMethodUnload support // pending CompiledMethodUnload support
// //
bool JvmtiExport::_have_pending_compiled_method_unload_events; void JvmtiExport::post_compiled_method_unload(
GrowableArray<jmethodID>* JvmtiExport::_pending_compiled_method_unload_method_ids; jmethodID method, const void *code_begin) {
GrowableArray<const void *>* JvmtiExport::_pending_compiled_method_unload_code_begins; JavaThread* thread = JavaThread::current();
JavaThread* JvmtiExport::_current_poster;
void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethodID method, const void *code_begin) {
EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
("JVMTI [%s] method compile unload event triggered", ("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. // post the event for each environment that has this event enabled.
JvmtiEnvIterator it; JvmtiEnvIterator it;
...@@ -767,12 +764,12 @@ void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethod ...@@ -767,12 +764,12 @@ void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethod
EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT, ("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); JvmtiEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(self); JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload; jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload;
if (callback != NULL) { if (callback != NULL) {
(*callback)(env->jvmti_external(), method, code_begin); (*callback)(env->jvmti_external(), method, code_begin);
...@@ -781,90 +778,6 @@ void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethod ...@@ -781,90 +778,6 @@ void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethod
} }
} }
// 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 // JvmtiExport
...@@ -1830,16 +1743,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) { ...@@ -1830,16 +1743,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) {
} }
void JvmtiExport::post_compiled_method_load(nmethod *nm) { 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(); 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, EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD,
("JVMTI [%s] method compile load event triggered", ("JVMTI [%s] method compile load event triggered",
...@@ -1854,8 +1758,8 @@ void JvmtiExport::post_compiled_method_load(nmethod *nm) { ...@@ -1854,8 +1758,8 @@ void JvmtiExport::post_compiled_method_load(nmethod *nm) {
JvmtiTrace::safe_get_thread_name(thread), JvmtiTrace::safe_get_thread_name(thread),
(nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(), (nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(),
(nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string())); (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string()));
ResourceMark rm(thread); ResourceMark rm(thread);
HandleMark hm(thread);
// Add inlining information // Add inlining information
jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm);
...@@ -1899,28 +1803,6 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, const jmethodID metho ...@@ -1899,28 +1803,6 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, const jmethodID metho
} }
} }
// 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) { void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) {
JavaThread* thread = JavaThread::current(); JavaThread* thread = JavaThread::current();
EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
...@@ -1953,9 +1835,9 @@ void JvmtiExport::post_dynamic_code_generated(const char *name, const void *code ...@@ -1953,9 +1835,9 @@ void JvmtiExport::post_dynamic_code_generated(const char *name, const void *code
return; return;
} }
if (have_pending_compiled_method_unload_events()) { // Blocks until everything now in the queue has been posted
post_pending_compiled_method_unload_events(); JvmtiDeferredEventQueue::flush_queue(Thread::current());
}
post_dynamic_code_generated_internal(name, code_begin, code_end); post_dynamic_code_generated_internal(name, code_begin, code_end);
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -141,25 +141,6 @@ class JvmtiExport : public AllStatic { ...@@ -141,25 +141,6 @@ class JvmtiExport : public AllStatic {
private: 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). // posts a DynamicCodeGenerated event (internal/private implementation).
// The public post_dynamic_code_generated* functions make use of the // The public post_dynamic_code_generated* functions make use of the
// internal implementation. // internal implementation.
...@@ -256,7 +237,7 @@ class JvmtiExport : public AllStatic { ...@@ -256,7 +237,7 @@ class JvmtiExport : public AllStatic {
// single stepping management methods // single stepping management methods
static void at_single_stepping_point(JavaThread *thread, methodOop method, address location) KERNEL_RETURN; static void at_single_stepping_point(JavaThread *thread, methodOop method, address location) KERNEL_RETURN;
static void expose_single_stepping(JavaThread *thread) 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. // Methods that notify the debugger that something interesting has happened in the VM.
static void post_vm_start (); static void post_vm_start ();
...@@ -271,20 +252,20 @@ class JvmtiExport : public AllStatic { ...@@ -271,20 +252,20 @@ class JvmtiExport : public AllStatic {
static oop jni_GetField_probe (JavaThread *thread, jobject jobj, static oop jni_GetField_probe (JavaThread *thread, jobject jobj,
oop obj, klassOop klass, jfieldID fieldID, bool is_static) 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, static oop jni_GetField_probe_nh (JavaThread *thread, jobject jobj,
oop obj, klassOop klass, jfieldID fieldID, bool is_static) 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, static void post_field_access_by_jni (JavaThread *thread, oop obj,
klassOop klass, jfieldID fieldID, bool is_static) KERNEL_RETURN; klassOop klass, jfieldID fieldID, bool is_static) KERNEL_RETURN;
static void post_field_access (JavaThread *thread, methodOop method, static void post_field_access (JavaThread *thread, methodOop method,
address location, KlassHandle field_klass, Handle object, jfieldID field) KERNEL_RETURN; address location, KlassHandle field_klass, Handle object, jfieldID field) KERNEL_RETURN;
static oop jni_SetField_probe (JavaThread *thread, jobject jobj, static oop jni_SetField_probe (JavaThread *thread, jobject jobj,
oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type, 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, static oop jni_SetField_probe_nh (JavaThread *thread, jobject jobj,
oop obj, klassOop klass, jfieldID fieldID, bool is_static, char sig_type, 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, static void post_field_modification_by_jni(JavaThread *thread, oop obj,
klassOop klass, jfieldID fieldID, bool is_static, char sig_type, klassOop klass, jfieldID fieldID, bool is_static, char sig_type,
jvalue *value); jvalue *value);
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -32,11 +32,13 @@ ...@@ -32,11 +32,13 @@
#include "prims/jvmtiEventController.inline.hpp" #include "prims/jvmtiEventController.inline.hpp"
#include "prims/jvmtiImpl.hpp" #include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiRedefineClasses.hpp" #include "prims/jvmtiRedefineClasses.hpp"
#include "runtime/atomic.hpp"
#include "runtime/deoptimization.hpp" #include "runtime/deoptimization.hpp"
#include "runtime/handles.hpp" #include "runtime/handles.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.hpp" #include "runtime/interfaceSupport.hpp"
#include "runtime/javaCalls.hpp" #include "runtime/javaCalls.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/signature.hpp" #include "runtime/signature.hpp"
#include "runtime/vframe.hpp" #include "runtime/vframe.hpp"
#include "runtime/vframe_hp.hpp" #include "runtime/vframe_hp.hpp"
...@@ -910,3 +912,207 @@ void JvmtiSuspendControl::print() { ...@@ -910,3 +912,207 @@ void JvmtiSuspendControl::print() {
tty->print_cr("]"); tty->print_cr("]");
#endif #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
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -433,6 +433,149 @@ public: ...@@ -433,6 +433,149 @@ public:
#endif // !JVMTI_KERNEL #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: // Utility macro that checks for NULL pointers:
#define NULL_CHECK(X, Y) if ((X) == NULL) { return (Y); } #define NULL_CHECK(X, Y) if ((X) == NULL) { return (Y); }
......
...@@ -231,13 +231,13 @@ public: ...@@ -231,13 +231,13 @@ public:
static void engage(JavaThread* mainThread, bool fullProfile) KERNEL_RETURN ; static void engage(JavaThread* mainThread, bool fullProfile) KERNEL_RETURN ;
static void disengage() KERNEL_RETURN ; static void disengage() KERNEL_RETURN ;
static void print(int unused) 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, // This is NULL if each thread has its own thread profiler,
// else this is the single thread profiler used by all threads. // else this is the single thread profiler used by all threads.
// In particular it makes a difference during garbage collection, // In particular it makes a difference during garbage collection,
// where you only want to traverse each thread profiler once. // 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 // Garbage Collection Support
static void oops_do(OopClosure* f) KERNEL_RETURN ; static void oops_do(OopClosure* f) KERNEL_RETURN ;
...@@ -246,13 +246,13 @@ public: ...@@ -246,13 +246,13 @@ public:
// Returns the start address for a given pc // Returns the start address for a given pc
// NULL is returned if the PCRecorder is inactive // 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 enum { MillisecsPerTick = 10 }; // ms per profiling ticks
// Returns the number of ticks recorded for the bucket // Returns the number of ticks recorded for the bucket
// pc belongs to. // 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 #ifndef FPROF_KERNEL
......
...@@ -129,7 +129,7 @@ Mutex* HotCardCache_lock = NULL; ...@@ -129,7 +129,7 @@ Mutex* HotCardCache_lock = NULL;
Monitor* GCTaskManager_lock = NULL; Monitor* GCTaskManager_lock = NULL;
Mutex* Management_lock = NULL; Mutex* Management_lock = NULL;
Monitor* LowMemory_lock = NULL; Monitor* Service_lock = NULL;
#define MAX_NUM_MUTEX 128 #define MAX_NUM_MUTEX 128
static Monitor * _mutex_array[MAX_NUM_MUTEX]; static Monitor * _mutex_array[MAX_NUM_MUTEX];
...@@ -203,7 +203,7 @@ void mutex_init() { ...@@ -203,7 +203,7 @@ void mutex_init() {
def(Patching_lock , Mutex , special, true ); // used for safepointing and code patching. def(Patching_lock , Mutex , special, true ); // used for safepointing and code patching.
def(ObjAllocPost_lock , Monitor, special, false); 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(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs.
def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread
......
...@@ -131,7 +131,7 @@ extern Mutex* MMUTracker_lock; // protects the MMU ...@@ -131,7 +131,7 @@ extern Mutex* MMUTracker_lock; // protects the MMU
extern Mutex* HotCardCache_lock; // protects the hot card cache extern Mutex* HotCardCache_lock; // protects the hot card cache
extern Mutex* Management_lock; // a lock used to serialize JVM management 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 // 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 // for the scope which contains the locker. The lock is an OS lock, not
......
/*
* 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;
}
/*
* 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
...@@ -1680,16 +1680,6 @@ inline size_t JavaThread::stack_available(address cur_sp) { ...@@ -1680,16 +1680,6 @@ inline size_t JavaThread::stack_available(address cur_sp) {
return cur_sp > low_addr ? cur_sp - low_addr : 0; 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. // A thread used for Compilation.
class CompilerThread : public JavaThread { class CompilerThread : public JavaThread {
friend class VMStructs; friend class VMStructs;
......
...@@ -93,6 +93,7 @@ ...@@ -93,6 +93,7 @@
#include "runtime/java.hpp" #include "runtime/java.hpp"
#include "runtime/javaCalls.hpp" #include "runtime/javaCalls.hpp"
#include "runtime/perfMemory.hpp" #include "runtime/perfMemory.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/sharedRuntime.hpp" #include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp" #include "runtime/stubRoutines.hpp"
#include "runtime/virtualspace.hpp" #include "runtime/virtualspace.hpp"
...@@ -1250,7 +1251,7 @@ static inline uint64_t cast_uint64_t(size_t x) ...@@ -1250,7 +1251,7 @@ static inline uint64_t cast_uint64_t(size_t x)
declare_type(WatcherThread, Thread) \ declare_type(WatcherThread, Thread) \
declare_type(JavaThread, Thread) \ declare_type(JavaThread, Thread) \
declare_type(JvmtiAgentThread, JavaThread) \ declare_type(JvmtiAgentThread, JavaThread) \
declare_type(LowMemoryDetectorThread, JavaThread) \ declare_type(ServiceThread, JavaThread) \
declare_type(CompilerThread, JavaThread) \ declare_type(CompilerThread, JavaThread) \
declare_toplevel_type(OSThread) \ declare_toplevel_type(OSThread) \
declare_toplevel_type(JavaFrameAnchor) \ declare_toplevel_type(JavaFrameAnchor) \
......
...@@ -59,10 +59,10 @@ class AttachListener: AllStatic { ...@@ -59,10 +59,10 @@ class AttachListener: AllStatic {
static void detachall() KERNEL_RETURN; static void detachall() KERNEL_RETURN;
// indicates if the Attach Listener needs to be created at startup // 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 // 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 #ifdef SERVICES_KERNEL
static bool is_attach_supported() { return false; } static bool is_attach_supported() { return false; }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,55 +34,11 @@ ...@@ -34,55 +34,11 @@
#include "services/lowMemoryDetector.hpp" #include "services/lowMemoryDetector.hpp"
#include "services/management.hpp" #include "services/management.hpp"
LowMemoryDetectorThread* LowMemoryDetector::_detector_thread = NULL;
volatile bool LowMemoryDetector::_enabled_for_collected_pools = false; volatile bool LowMemoryDetector::_enabled_for_collected_pools = false;
volatile jint LowMemoryDetector::_disabled_count = 0; 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() { 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; bool has_requests = false;
int num_memory_pools = MemoryService::num_memory_pools(); int num_memory_pools = MemoryService::num_memory_pools();
for (int i = 0; i < num_memory_pools; i++) { for (int i = 0; i < num_memory_pools; i++) {
...@@ -100,35 +56,11 @@ bool LowMemoryDetector::has_pending_requests() { ...@@ -100,35 +56,11 @@ bool LowMemoryDetector::has_pending_requests() {
return has_requests; return has_requests;
} }
void LowMemoryDetector::low_memory_detector_thread_entry(JavaThread* jt, TRAPS) { void LowMemoryDetector::process_sensor_changes(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);
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);
}
}
{
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
HandleMark hm(THREAD); HandleMark hm(THREAD);
// No need to hold LowMemory_lock to call out to Java // No need to hold Service_lock to call out to Java
int num_memory_pools = MemoryService::num_memory_pools(); int num_memory_pools = MemoryService::num_memory_pools();
for (int i = 0; i < num_memory_pools; i++) { for (int i = 0; i < num_memory_pools; i++) {
MemoryPool* pool = MemoryService::get_memory_pool(i); MemoryPool* pool = MemoryService::get_memory_pool(i);
...@@ -141,14 +73,12 @@ void LowMemoryDetector::low_memory_detector_thread_entry(JavaThread* jt, TRAPS) ...@@ -141,14 +73,12 @@ void LowMemoryDetector::low_memory_detector_thread_entry(JavaThread* jt, TRAPS)
gc_sensor->process_pending_requests(CHECK); gc_sensor->process_pending_requests(CHECK);
} }
} }
}
}
} }
// This method could be called from any Java threads // This method could be called from any Java threads
// and also VMThread. // and also VMThread.
void LowMemoryDetector::detect_low_memory() { 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; bool has_pending_requests = false;
int num_memory_pools = MemoryService::num_memory_pools(); int num_memory_pools = MemoryService::num_memory_pools();
...@@ -166,7 +96,7 @@ void LowMemoryDetector::detect_low_memory() { ...@@ -166,7 +96,7 @@ void LowMemoryDetector::detect_low_memory() {
} }
if (has_pending_requests) { if (has_pending_requests) {
LowMemory_lock->notify_all(); Service_lock->notify_all();
} }
} }
...@@ -181,14 +111,14 @@ void LowMemoryDetector::detect_low_memory(MemoryPool* pool) { ...@@ -181,14 +111,14 @@ void LowMemoryDetector::detect_low_memory(MemoryPool* pool) {
} }
{ {
MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
MemoryUsage usage = pool->get_memory_usage(); MemoryUsage usage = pool->get_memory_usage();
sensor->set_gauge_sensor_level(usage, sensor->set_gauge_sensor_level(usage,
pool->usage_threshold()); pool->usage_threshold());
if (sensor->has_pending_requests()) { if (sensor->has_pending_requests()) {
// notify sensor state update // notify sensor state update
LowMemory_lock->notify_all(); Service_lock->notify_all();
} }
} }
} }
...@@ -203,14 +133,14 @@ void LowMemoryDetector::detect_after_gc_memory(MemoryPool* pool) { ...@@ -203,14 +133,14 @@ void LowMemoryDetector::detect_after_gc_memory(MemoryPool* pool) {
} }
{ {
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(); MemoryUsage usage = pool->get_last_collection_usage();
sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold()); sensor->set_counter_sensor_level(usage, pool->gc_usage_threshold());
if (sensor->has_pending_requests()) { if (sensor->has_pending_requests()) {
// notify sensor state update // notify sensor state update
LowMemory_lock->notify_all(); Service_lock->notify_all();
} }
} }
} }
...@@ -384,8 +314,8 @@ void SensorInfo::trigger(int count, TRAPS) { ...@@ -384,8 +314,8 @@ void SensorInfo::trigger(int count, TRAPS) {
} }
{ {
// Holds LowMemory_lock and update the sensor state // Holds Service_lock and update the sensor state
MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
_sensor_on = true; _sensor_on = true;
_sensor_count += count; _sensor_count += count;
_pending_trigger_count = _pending_trigger_count - count; _pending_trigger_count = _pending_trigger_count - count;
...@@ -410,8 +340,8 @@ void SensorInfo::clear(int count, TRAPS) { ...@@ -410,8 +340,8 @@ void SensorInfo::clear(int count, TRAPS) {
} }
{ {
// Holds LowMemory_lock and update the sensor state // Holds Service_lock and update the sensor state
MutexLockerEx ml(LowMemory_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
_sensor_on = false; _sensor_on = false;
_pending_clear_count = 0; _pending_clear_count = 0;
_pending_trigger_count = _pending_trigger_count - count; _pending_trigger_count = _pending_trigger_count - count;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -58,8 +58,8 @@ ...@@ -58,8 +58,8 @@
// //
// May need to deal with hysteresis effect. // May need to deal with hysteresis effect.
// //
// Memory detection code runs in the Service thread (serviceThread.hpp).
class LowMemoryDetectorThread;
class OopClosure; class OopClosure;
class MemoryPool; class MemoryPool;
...@@ -211,23 +211,22 @@ public: ...@@ -211,23 +211,22 @@ public:
}; };
class LowMemoryDetector : public AllStatic { class LowMemoryDetector : public AllStatic {
friend class LowMemoryDetectorDisabler; friend class LowMemoryDetectorDisabler;
friend class ServiceThread;
private: private:
// true if any collected heap has low memory detection enabled // true if any collected heap has low memory detection enabled
static volatile bool _enabled_for_collected_pools; static volatile bool _enabled_for_collected_pools;
// > 0 if temporary disabed // > 0 if temporary disabed
static volatile jint _disabled_count; 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 void check_memory_usage();
static bool has_pending_requests(); static bool has_pending_requests();
static bool temporary_disabled() { return _disabled_count > 0; } static bool temporary_disabled() { return _disabled_count > 0; }
static void disable() { Atomic::inc(&_disabled_count); } static void disable() { Atomic::inc(&_disabled_count); }
static void enable() { Atomic::dec(&_disabled_count); } static void enable() { Atomic::dec(&_disabled_count); }
static void process_sensor_changes(TRAPS);
public: public:
static void initialize();
static void detect_low_memory(); static void detect_low_memory();
static void detect_low_memory(MemoryPool* pool); static void detect_low_memory(MemoryPool* pool);
static void detect_after_gc_memory(MemoryPool* pool); static void detect_after_gc_memory(MemoryPool* pool);
...@@ -275,7 +274,6 @@ public: ...@@ -275,7 +274,6 @@ public:
} }
} }
} }
}; };
class LowMemoryDetectorDisabler: public StackObj { class LowMemoryDetectorDisabler: public StackObj {
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "runtime/javaCalls.hpp" #include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.hpp" #include "runtime/jniHandles.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/serviceThread.hpp"
#include "services/classLoadingService.hpp" #include "services/classLoadingService.hpp"
#include "services/heapDumper.hpp" #include "services/heapDumper.hpp"
#include "services/lowMemoryDetector.hpp" #include "services/lowMemoryDetector.hpp"
...@@ -112,8 +113,8 @@ void Management::init() { ...@@ -112,8 +113,8 @@ void Management::init() {
} }
void Management::initialize(TRAPS) { void Management::initialize(TRAPS) {
// Start the low memory detector thread // Start the service thread
LowMemoryDetector::initialize(); ServiceThread::initialize();
if (ManagementServer) { if (ManagementServer) {
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#define SERVICES_KERNEL #define SERVICES_KERNEL
#define KERNEL_RETURN {} #define KERNEL_RETURN {}
#define KERNEL_RETURN_(code) { code } #define KERNEL_RETURN_(code) { return code; }
#else // KERNEL #else // KERNEL
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册