@@ -2764,42 +2764,59 @@ void CompileBroker::print_heapinfo(outputStream* out, const char* function, size
2764
2764
}
2765
2765
2766
2766
// We hold the CodeHeapStateAnalytics_lock all the time, from here until we leave this function.
2767
- // That prevents another thread from destroying our view on the CodeHeap.
2767
+ // That prevents other threads from destroying (making inconsistent) our view on the CodeHeap.
2768
2768
// When we request individual parts of the analysis via the jcmd interface, it is possible
2769
2769
// that in between another thread (another jcmd user or the vm running into CodeCache OOM)
2770
- // updated the aggregated data. That's a tolerable tradeoff because we can't hold a lock
2771
- // across user interaction.
2772
- // Acquire this lock before acquiring the CodeCache_lock.
2773
- // CodeHeapStateAnalytics_lock could be held by a concurrent thread for a long time,
2774
- // leading to an unnecessarily long hold time of the CodeCache_lock.
2770
+ // updated the aggregated data. We will then see a modified, but again consistent, view
2771
+ // on the CodeHeap. That's a tolerable tradeoff we have to accept because we can't hold
2772
+ // a lock across user interaction.
2773
+
2774
+ // We should definitely acquire this lock before acquiring Compile_lock and CodeCache_lock.
2775
+ // CodeHeapStateAnalytics_lock may be held by a concurrent thread for a long time,
2776
+ // leading to an unnecessarily long hold time of the other locks we acquired before.
2775
2777
ts.update (); // record starting point
2776
- MutexLocker mu1 (CodeHeapStateAnalytics_lock, Mutex::_no_safepoint_check_flag );
2778
+ MutexLocker mu0 (CodeHeapStateAnalytics_lock, Mutex::_safepoint_check_flag );
2777
2779
out->print_cr (" \n __ CodeHeapStateAnalytics lock wait took %10.3f seconds _________\n " , ts.seconds ());
2778
2780
2779
- // If we serve an "allFun" call, it is beneficial to hold the CodeCache_lock
2780
- // for the entire duration of aggregation and printing. That makes sure
2781
- // we see a consistent picture and do not run into issues caused by
2782
- // the CodeHeap being altered concurrently.
2783
- Mutex* global_lock = allFun ? CodeCache_lock : NULL ;
2784
- Mutex* function_lock = allFun ? NULL : CodeCache_lock;
2781
+ // Holding the CodeCache_lock protects from concurrent alterations of the CodeCache.
2782
+ // Unfortunately, such protection is not sufficient:
2783
+ // When a new nmethod is created via ciEnv::register_method(), the
2784
+ // Compile_lock is taken first. After some initializations,
2785
+ // nmethod::new_nmethod() takes over, grabbing the CodeCache_lock
2786
+ // immediately (after finalizing the oop references). To lock out concurrent
2787
+ // modifiers, we have to grab both locks as well in the described sequence.
2788
+ //
2789
+ // If we serve an "allFun" call, it is beneficial to hold CodeCache_lock and Compile_lock
2790
+ // for the entire duration of aggregation and printing. That makes sure we see
2791
+ // a consistent picture and do not run into issues caused by concurrent alterations.
2792
+ bool should_take_Compile_lock = !SafepointSynchronize::is_at_safepoint () &&
2793
+ !Compile_lock->owned_by_self ();
2794
+ bool should_take_CodeCache_lock = !SafepointSynchronize::is_at_safepoint () &&
2795
+ !CodeCache_lock->owned_by_self ();
2796
+ Mutex* global_lock_1 = allFun ? (should_take_Compile_lock ? Compile_lock : NULL ) : NULL ;
2797
+ Monitor* global_lock_2 = allFun ? (should_take_CodeCache_lock ? CodeCache_lock : NULL ) : NULL ;
2798
+ Mutex* function_lock_1 = allFun ? NULL : (should_take_Compile_lock ? Compile_lock : NULL );
2799
+ Monitor* function_lock_2 = allFun ? NULL : (should_take_CodeCache_lock ? CodeCache_lock : NULL );
2785
2800
ts_global.update (); // record starting point
2786
- MutexLocker mu2 (global_lock, Mutex::_no_safepoint_check_flag);
2787
- if (global_lock != NULL ) {
2788
- out->print_cr (" \n __ CodeCache (global) lock wait took %10.3f seconds _________\n " , ts_global.seconds ());
2801
+ MutexLocker mu1 (global_lock_1, Mutex::_safepoint_check_flag);
2802
+ MutexLocker mu2 (global_lock_2, Mutex::_no_safepoint_check_flag);
2803
+ if ((global_lock_1 != NULL ) || (global_lock_2 != NULL )) {
2804
+ out->print_cr (" \n __ Compile & CodeCache (global) lock wait took %10.3f seconds _________\n " , ts_global.seconds ());
2789
2805
ts_global.update (); // record starting point
2790
2806
}
2791
2807
2792
2808
if (aggregate) {
2793
2809
ts.update (); // record starting point
2794
- MutexLocker mu3 (function_lock, Mutex::_no_safepoint_check_flag);
2795
- if (function_lock != NULL ) {
2796
- out->print_cr (" \n __ CodeCache (function) lock wait took %10.3f seconds _________\n " , ts.seconds ());
2810
+ MutexLocker mu11 (function_lock_1, Mutex::_safepoint_check_flag);
2811
+ MutexLocker mu22 (function_lock_2, Mutex::_no_safepoint_check_flag);
2812
+ if ((function_lock_1 != NULL ) || (function_lock_1 != NULL )) {
2813
+ out->print_cr (" \n __ Compile & CodeCache (function) lock wait took %10.3f seconds _________\n " , ts.seconds ());
2797
2814
}
2798
2815
2799
2816
ts.update (); // record starting point
2800
2817
CodeCache::aggregate (out, granularity);
2801
- if (function_lock != NULL ) {
2802
- out->print_cr (" \n __ CodeCache (function) lock hold took %10.3f seconds _________\n " , ts.seconds ());
2818
+ if ((function_lock_1 != NULL ) || (function_lock_1 != NULL ) ) {
2819
+ out->print_cr (" \n __ Compile & CodeCache (function) lock hold took %10.3f seconds _________\n " , ts.seconds ());
2803
2820
}
2804
2821
}
2805
2822
@@ -2809,15 +2826,18 @@ void CompileBroker::print_heapinfo(outputStream* out, const char* function, size
2809
2826
if (methodSpace) CodeCache::print_space (out);
2810
2827
if (methodAge) CodeCache::print_age (out);
2811
2828
if (methodNames) {
2812
- // print_names() has shown to be sensitive to concurrent CodeHeap modifications.
2813
- // Therefore, request the CodeCache_lock before calling...
2814
- MutexLocker mu3 (function_lock, Mutex::_no_safepoint_check_flag);
2815
- CodeCache::print_names (out);
2829
+ if (allFun) {
2830
+ // print_names() can only be used safely if the locks have been continuously held
2831
+ // since aggregation begin. That is true only for function "all".
2832
+ CodeCache::print_names (out);
2833
+ } else {
2834
+ out->print_cr (" \n CodeHeapStateAnalytics: Function 'MethodNames' is only available as part of function 'all'" );
2835
+ }
2816
2836
}
2817
2837
if (discard) CodeCache::discard (out);
2818
2838
2819
- if (global_lock != NULL ) {
2820
- out->print_cr (" \n __ CodeCache (global) lock hold took %10.3f seconds _________\n " , ts_global.seconds ());
2839
+ if ((global_lock_1 != NULL ) || (global_lock_2 != NULL ) ) {
2840
+ out->print_cr (" \n __ Compile & CodeCache (global) lock hold took %10.3f seconds _________\n " , ts_global.seconds ());
2821
2841
}
2822
2842
out->print_cr (" \n __ CodeHeapStateAnalytics total duration %10.3f seconds _________\n " , ts_total.seconds ());
2823
2843
}
0 commit comments