Mercurial > hg > graal-compiler
view src/share/vm/services/threadService.hpp @ 2149:7e37af9d69ef
7011379: G1: overly long concurrent marking cycles
Summary: This changeset introduces filtering of SATB buffers at the point when they are about to be enqueued. If this filtering clears enough entries on each buffer, the buffer can then be re-used and not enqueued. This cuts down the number of SATB buffers that need to be processed by the concurrent marking threads.
Reviewed-by: johnc, ysr
author | tonyp |
---|---|
date | Wed, 19 Jan 2011 09:35:17 -0500 |
parents | b1a2afa37ec4 |
children | d2a62e0f25eb |
line wrap: on
line source
/* * 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. * */ #ifndef SHARE_VM_SERVICES_THREADSERVICE_HPP #define SHARE_VM_SERVICES_THREADSERVICE_HPP #include "classfile/javaClasses.hpp" #include "runtime/handles.hpp" #include "runtime/init.hpp" #include "runtime/jniHandles.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/perfData.hpp" #include "services/management.hpp" #include "services/serviceUtil.hpp" class OopClosure; class ThreadDumpResult; class ThreadStackTrace; class ThreadSnapshot; class StackFrameInfo; class ThreadConcurrentLocks; class DeadlockCycle; // VM monitoring and management support for the thread and // synchronization subsystem // // Thread contention monitoring is disabled by default. // When enabled, the VM will begin measuring the accumulated // elapsed time a thread blocked on synchronization. // class ThreadService : public AllStatic { private: // These counters could be moved to Threads class static PerfCounter* _total_threads_count; static PerfVariable* _live_threads_count; static PerfVariable* _peak_threads_count; static PerfVariable* _daemon_threads_count; // These 2 counters are atomically incremented once the thread is exiting. // They will be atomically decremented when ThreadService::remove_thread is called. static volatile int _exiting_threads_count; static volatile int _exiting_daemon_threads_count; static bool _thread_monitoring_contention_enabled; static bool _thread_cpu_time_enabled; static bool _thread_allocated_memory_enabled; // Need to keep the list of thread dump result that // keep references to methodOop since thread dump can be // requested by multiple threads concurrently. static ThreadDumpResult* _threaddump_list; public: static void init(); static void add_thread(JavaThread* thread, bool daemon); static void remove_thread(JavaThread* thread, bool daemon); static void current_thread_exiting(JavaThread* jt); static bool set_thread_monitoring_contention(bool flag); static bool is_thread_monitoring_contention() { return _thread_monitoring_contention_enabled; } static bool set_thread_cpu_time_enabled(bool flag); static bool is_thread_cpu_time_enabled() { return _thread_cpu_time_enabled; } static bool set_thread_allocated_memory_enabled(bool flag); static bool is_thread_allocated_memory_enabled() { return _thread_cpu_time_enabled; } static jlong get_total_thread_count() { return _total_threads_count->get_value(); } static jlong get_peak_thread_count() { return _peak_threads_count->get_value(); } static jlong get_live_thread_count() { return _live_threads_count->get_value() - _exiting_threads_count; } static jlong get_daemon_thread_count() { return _daemon_threads_count->get_value() - _exiting_daemon_threads_count; } static int exiting_threads_count() { return _exiting_threads_count; } static int exiting_daemon_threads_count() { return _exiting_daemon_threads_count; } // Support for thread dump static void add_thread_dump(ThreadDumpResult* dump); static void remove_thread_dump(ThreadDumpResult* dump); static Handle get_current_contended_monitor(JavaThread* thread); // This function is called by JVM_DumpThreads. static Handle dump_stack_traces(GrowableArray<instanceHandle>* threads, int num_threads, TRAPS); static void reset_peak_thread_count(); static void reset_contention_count_stat(JavaThread* thread); static void reset_contention_time_stat(JavaThread* thread); static DeadlockCycle* find_deadlocks_at_safepoint(bool object_monitors_only); // GC support static void oops_do(OopClosure* f); }; // Per-thread Statistics for synchronization class ThreadStatistics : public CHeapObj { private: // The following contention statistics are only updated by // the thread owning these statistics when contention occurs. jlong _contended_enter_count; elapsedTimer _contended_enter_timer; jlong _monitor_wait_count; elapsedTimer _monitor_wait_timer; jlong _sleep_count; elapsedTimer _sleep_timer; // These two reset flags are set to true when another thread // requests to reset the statistics. The actual statistics // are reset when the thread contention occurs and attempts // to update the statistics. bool _count_pending_reset; bool _timer_pending_reset; // Keep accurate times for potentially recursive class operations int _perf_recursion_counts[6]; elapsedTimer _perf_timers[6]; // utility functions void check_and_reset_count() { if (!_count_pending_reset) return; _contended_enter_count = 0; _monitor_wait_count = 0; _sleep_count = 0; _count_pending_reset = 0; } void check_and_reset_timer() { if (!_timer_pending_reset) return; _contended_enter_timer.reset(); _monitor_wait_timer.reset(); _sleep_timer.reset(); _timer_pending_reset = 0; } public: ThreadStatistics(); jlong contended_enter_count() { return (_count_pending_reset ? 0 : _contended_enter_count); } jlong contended_enter_ticks() { return (_timer_pending_reset ? 0 : _contended_enter_timer.active_ticks()); } jlong monitor_wait_count() { return (_count_pending_reset ? 0 : _monitor_wait_count); } jlong monitor_wait_ticks() { return (_timer_pending_reset ? 0 : _monitor_wait_timer.active_ticks()); } jlong sleep_count() { return (_count_pending_reset ? 0 : _sleep_count); } jlong sleep_ticks() { return (_timer_pending_reset ? 0 : _sleep_timer.active_ticks()); } void monitor_wait() { check_and_reset_count(); _monitor_wait_count++; } void monitor_wait_begin() { check_and_reset_timer(); _monitor_wait_timer.start(); } void monitor_wait_end() { _monitor_wait_timer.stop(); check_and_reset_timer(); } void thread_sleep() { check_and_reset_count(); _sleep_count++; } void thread_sleep_begin() { check_and_reset_timer(); _sleep_timer.start(); } void thread_sleep_end() { _sleep_timer.stop(); check_and_reset_timer(); } void contended_enter() { check_and_reset_count(); _contended_enter_count++; } void contended_enter_begin() { check_and_reset_timer(); _contended_enter_timer.start(); } void contended_enter_end() { _contended_enter_timer.stop(); check_and_reset_timer(); } void reset_count_stat() { _count_pending_reset = true; } void reset_time_stat() { _timer_pending_reset = true; } int* perf_recursion_counts_addr() { return _perf_recursion_counts; } elapsedTimer* perf_timers_addr() { return _perf_timers; } }; // Thread snapshot to represent the thread state and statistics class ThreadSnapshot : public CHeapObj { private: JavaThread* _thread; oop _threadObj; java_lang_Thread::ThreadStatus _thread_status; bool _is_ext_suspended; bool _is_in_native; jlong _contended_enter_ticks; jlong _contended_enter_count; jlong _monitor_wait_ticks; jlong _monitor_wait_count; jlong _sleep_ticks; jlong _sleep_count; oop _blocker_object; oop _blocker_object_owner; ThreadStackTrace* _stack_trace; ThreadConcurrentLocks* _concurrent_locks; ThreadSnapshot* _next; public: // Dummy snapshot ThreadSnapshot() : _thread(NULL), _threadObj(NULL), _stack_trace(NULL), _concurrent_locks(NULL), _next(NULL), _blocker_object(NULL), _blocker_object_owner(NULL) {}; ThreadSnapshot(JavaThread* thread); ~ThreadSnapshot(); java_lang_Thread::ThreadStatus thread_status() { return _thread_status; } oop threadObj() const { return _threadObj; } void set_next(ThreadSnapshot* n) { _next = n; } bool is_ext_suspended() { return _is_ext_suspended; } bool is_in_native() { return _is_in_native; } jlong contended_enter_count() { return _contended_enter_count; } jlong contended_enter_ticks() { return _contended_enter_ticks; } jlong monitor_wait_count() { return _monitor_wait_count; } jlong monitor_wait_ticks() { return _monitor_wait_ticks; } jlong sleep_count() { return _sleep_count; } jlong sleep_ticks() { return _sleep_ticks; } oop blocker_object() { return _blocker_object; } oop blocker_object_owner() { return _blocker_object_owner; } ThreadSnapshot* next() const { return _next; } ThreadStackTrace* get_stack_trace() { return _stack_trace; } ThreadConcurrentLocks* get_concurrent_locks() { return _concurrent_locks; } void dump_stack_at_safepoint(int max_depth, bool with_locked_monitors); void set_concurrent_locks(ThreadConcurrentLocks* l) { _concurrent_locks = l; } void oops_do(OopClosure* f); }; class ThreadStackTrace : public CHeapObj { private: JavaThread* _thread; int _depth; // number of stack frames added bool _with_locked_monitors; GrowableArray<StackFrameInfo*>* _frames; GrowableArray<oop>* _jni_locked_monitors; public: ThreadStackTrace(JavaThread* thread, bool with_locked_monitors); ~ThreadStackTrace(); JavaThread* thread() { return _thread; } StackFrameInfo* stack_frame_at(int i) { return _frames->at(i); } int get_stack_depth() { return _depth; } void add_stack_frame(javaVFrame* jvf); void dump_stack_at_safepoint(int max_depth); Handle allocate_fill_stack_trace_element_array(TRAPS); void oops_do(OopClosure* f); GrowableArray<oop>* jni_locked_monitors() { return _jni_locked_monitors; } int num_jni_locked_monitors() { return (_jni_locked_monitors != NULL ? _jni_locked_monitors->length() : 0); } bool is_owned_monitor_on_stack(oop object); void add_jni_locked_monitor(oop object) { _jni_locked_monitors->append(object); } }; // StackFrameInfo for keeping methodOop and bci during // stack walking for later construction of StackTraceElement[] // Java instances class StackFrameInfo : public CHeapObj { private: methodOop _method; int _bci; GrowableArray<oop>* _locked_monitors; // list of object monitors locked by this frame public: StackFrameInfo(javaVFrame* jvf, bool with_locked_monitors); ~StackFrameInfo() { if (_locked_monitors != NULL) { delete _locked_monitors; } }; methodOop method() const { return _method; } int bci() const { return _bci; } void oops_do(OopClosure* f); int num_locked_monitors() { return (_locked_monitors != NULL ? _locked_monitors->length() : 0); } GrowableArray<oop>* locked_monitors() { return _locked_monitors; } void print_on(outputStream* st) const; }; class ThreadConcurrentLocks : public CHeapObj { private: GrowableArray<instanceOop>* _owned_locks; ThreadConcurrentLocks* _next; JavaThread* _thread; public: ThreadConcurrentLocks(JavaThread* thread); ~ThreadConcurrentLocks(); void add_lock(instanceOop o); void set_next(ThreadConcurrentLocks* n) { _next = n; } ThreadConcurrentLocks* next() { return _next; } JavaThread* java_thread() { return _thread; } GrowableArray<instanceOop>* owned_locks() { return _owned_locks; } void oops_do(OopClosure* f); }; class ConcurrentLocksDump : public StackObj { private: ThreadConcurrentLocks* _map; ThreadConcurrentLocks* _last; // Last ThreadConcurrentLocks in the map bool _retain_map_on_free; void build_map(GrowableArray<oop>* aos_objects); void add_lock(JavaThread* thread, instanceOop o); public: ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {}; ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {}; ~ConcurrentLocksDump(); void dump_at_safepoint(); ThreadConcurrentLocks* thread_concurrent_locks(JavaThread* thread); void print_locks_on(JavaThread* t, outputStream* st); }; class ThreadDumpResult : public StackObj { private: int _num_threads; int _num_snapshots; ThreadSnapshot* _snapshots; ThreadSnapshot* _last; ThreadDumpResult* _next; public: ThreadDumpResult(); ThreadDumpResult(int num_threads); ~ThreadDumpResult(); void add_thread_snapshot(ThreadSnapshot* ts); void set_next(ThreadDumpResult* next) { _next = next; } ThreadDumpResult* next() { return _next; } int num_threads() { return _num_threads; } int num_snapshots() { return _num_snapshots; } ThreadSnapshot* snapshots() { return _snapshots; } void oops_do(OopClosure* f); }; class DeadlockCycle : public CHeapObj { private: bool _is_deadlock; GrowableArray<JavaThread*>* _threads; DeadlockCycle* _next; public: DeadlockCycle(); ~DeadlockCycle(); DeadlockCycle* next() { return _next; } void set_next(DeadlockCycle* d) { _next = d; } void add_thread(JavaThread* t) { _threads->append(t); } void reset() { _is_deadlock = false; _threads->clear(); } void set_deadlock(bool value) { _is_deadlock = value; } bool is_deadlock() { return _is_deadlock; } int num_threads() { return _threads->length(); } GrowableArray<JavaThread*>* threads() { return _threads; } void print_on(outputStream* st) const; }; // Utility class to get list of java threads. class ThreadsListEnumerator : public StackObj { private: GrowableArray<instanceHandle>* _threads_array; public: ThreadsListEnumerator(Thread* cur_thread, bool include_jvmti_agent_threads = false, bool include_jni_attaching_threads = true); int num_threads() { return _threads_array->length(); } instanceHandle get_threadObj(int index) { return _threads_array->at(index); } }; // abstract utility class to set new thread states, and restore previous after the block exits class JavaThreadStatusChanger : public StackObj { private: java_lang_Thread::ThreadStatus _old_state; JavaThread* _java_thread; bool _is_alive; void save_old_state(JavaThread* java_thread) { _java_thread = java_thread; _is_alive = is_alive(java_thread); if (is_alive()) { _old_state = java_lang_Thread::get_thread_status(_java_thread->threadObj()); } } public: static void set_thread_status(JavaThread* java_thread, java_lang_Thread::ThreadStatus state) { java_lang_Thread::set_thread_status(java_thread->threadObj(), state); } void set_thread_status(java_lang_Thread::ThreadStatus state) { if (is_alive()) { set_thread_status(_java_thread, state); } } JavaThreadStatusChanger(JavaThread* java_thread, java_lang_Thread::ThreadStatus state) { save_old_state(java_thread); set_thread_status(state); } JavaThreadStatusChanger(JavaThread* java_thread) { save_old_state(java_thread); } ~JavaThreadStatusChanger() { set_thread_status(_old_state); } static bool is_alive(JavaThread* java_thread) { return java_thread != NULL && java_thread->threadObj() != NULL; } bool is_alive() { return _is_alive; } }; // Change status to waiting on an object (timed or indefinite) class JavaThreadInObjectWaitState : public JavaThreadStatusChanger { private: ThreadStatistics* _stat; bool _active; public: JavaThreadInObjectWaitState(JavaThread *java_thread, bool timed) : JavaThreadStatusChanger(java_thread, timed ? java_lang_Thread::IN_OBJECT_WAIT_TIMED : java_lang_Thread::IN_OBJECT_WAIT) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); _stat->monitor_wait(); if (_active) { _stat->monitor_wait_begin(); } } else { _active = false; } } ~JavaThreadInObjectWaitState() { if (_active) { _stat->monitor_wait_end(); } } }; // Change status to parked (timed or indefinite) class JavaThreadParkedState : public JavaThreadStatusChanger { private: ThreadStatistics* _stat; bool _active; public: JavaThreadParkedState(JavaThread *java_thread, bool timed) : JavaThreadStatusChanger(java_thread, timed ? java_lang_Thread::PARKED_TIMED : java_lang_Thread::PARKED) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); _stat->monitor_wait(); if (_active) { _stat->monitor_wait_begin(); } } else { _active = false; } } ~JavaThreadParkedState() { if (_active) { _stat->monitor_wait_end(); } } }; // Change status to blocked on (re-)entering a synchronization block class JavaThreadBlockedOnMonitorEnterState : public JavaThreadStatusChanger { private: ThreadStatistics* _stat; bool _active; static bool contended_enter_begin(JavaThread *java_thread) { set_thread_status(java_thread, java_lang_Thread::BLOCKED_ON_MONITOR_ENTER); ThreadStatistics* stat = java_thread->get_thread_stat(); stat->contended_enter(); bool active = ThreadService::is_thread_monitoring_contention(); if (active) { stat->contended_enter_begin(); } return active; } public: // java_thread is waiting thread being blocked on monitor reenter. // Current thread is the notifying thread which holds the monitor. static bool wait_reenter_begin(JavaThread *java_thread, ObjectMonitor *obj_m) { assert((java_thread != NULL), "Java thread should not be null here"); bool active = false; if (is_alive(java_thread) && ServiceUtil::visible_oop((oop)obj_m->object())) { active = contended_enter_begin(java_thread); } return active; } static void wait_reenter_end(JavaThread *java_thread, bool active) { if (active) { java_thread->get_thread_stat()->contended_enter_end(); } set_thread_status(java_thread, java_lang_Thread::RUNNABLE); } JavaThreadBlockedOnMonitorEnterState(JavaThread *java_thread, ObjectMonitor *obj_m) : JavaThreadStatusChanger(java_thread) { assert((java_thread != NULL), "Java thread should not be null here"); // Change thread status and collect contended enter stats for monitor contended // enter done for external java world objects and it is contended. All other cases // like for vm internal objects and for external objects which are not contended // thread status is not changed and contended enter stat is not collected. _active = false; if (is_alive() && ServiceUtil::visible_oop((oop)obj_m->object()) && obj_m->contentions() > 0) { _stat = java_thread->get_thread_stat(); _active = contended_enter_begin(java_thread); } } ~JavaThreadBlockedOnMonitorEnterState() { if (_active) { _stat->contended_enter_end(); } } }; // Change status to sleeping class JavaThreadSleepState : public JavaThreadStatusChanger { private: ThreadStatistics* _stat; bool _active; public: JavaThreadSleepState(JavaThread *java_thread) : JavaThreadStatusChanger(java_thread, java_lang_Thread::SLEEPING) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); _stat->thread_sleep(); if (_active) { _stat->thread_sleep_begin(); } } else { _active = false; } } ~JavaThreadSleepState() { if (_active) { _stat->thread_sleep_end(); } } }; #endif // SHARE_VM_SERVICES_THREADSERVICE_HPP