RFR: 8185005: Improve performance of ThreadMXBean.getThreadInfo(long ids[], int maxDepth)

David Holmes david.holmes at oracle.com
Sat Sep 21 06:53:09 UTC 2019


On 21/09/2019 4:21 pm, serguei.spitsyn at oracle.com wrote:
> Hi David,
> 
> I do not want to insist in the area (Threads_lock use) where you have 
> much more expertise.

It's more conservatism than expertise :) I had a look through and there 
are not too many usages of the Threads_lock in the M&M code and they do 
appear safe. But I still prefer on principle to not hold nested locks if 
it is not necessary.

> Also, I agreed that Daniil's webrev.07 version is correct and less risky.
> The only my concern was about possible performance impact of linear 
> searches
> from the ThreadsList::find_JavaThread_from_java_tid() calls that can be 
> executed
> concurrently with the ThreadTable::lazy_initialize() if the ThreadsList 
> is big.
> But it should not be that big issue.

I think we can waste a lot of time wondering about what kinds of 
scenarios perform better or worse with which variant of the code. You 
will always be able to create a microbenchmark for which a particular 
code pattern works best. So I'd say stick to the simplest and least 
risky code and worry about performance issues like this if, or when, 
they turn up.

> So, I'm okay with the webrev.07 modulo ThreadTable renaming.

Okay.

Thanks,
David

> Thanks,
> Serguei
> 
> 
> On 9/20/19 19:20, David Holmes wrote:
>> Hi Serguei,
>>
>> On 21/09/2019 9:50 am, serguei.spitsyn at oracle.com wrote:
>>> Hi Daniil,
>>>
>>> Yes, the Threads_lock is still needed around thread->is_exiting() 
>>> check and add_thread().
>>>
>>>> And if we try to use 2 locks, ThreadTableCreate_lock as in your snippet
>>>> and then the nested Threads_lock around thread->is_exiting() and
>>>> add_thread(java_tid, thread) lines then it will not work since the rank
>>>> of Threads_lock  is higher than the rank of ThreadTableCreate_lock.
>>>
>>> The ThreadTableCreate_lock is only used in the lazy_initialize() 
>>> function.
>>> It looks safe to adjust the ThreadTableCreate_lock to fix the above 
>>> problem.
>>> Then the approach below will work as needed.
>>>
>>>   void ThreadTable::lazy_initialize(const ThreadsList *threads) {
>>>      if (_is_initialized) {
>>>        return;
>>>      }
>>>      MutexLocker ml(ThreadTableCreate_lock);
>>>      if (_is_initialized) {
>>>        // There is no obvious benefits in allowing the thread table
>>>        // being concurrently populated during the initalization.
>>>        return;
>>>      }
>>>      create_table(threads->length());
>>>      _is_initialized = true;
>>>
>>>      for (uint i = 0; i < threads->length(); i++) {
>>>        JavaThread* thread = threads->thread_at(i);
>>>        oop tobj = thread->threadObj();
>>>        if (tobj != NULL) {
>>>          jlong java_tid = java_lang_Thread::thread_id(tobj);
>>>          MutexLocker ml(Threads_lock);
>>>          if (!thread->is_exiting()) {
>>>            // Must be inside the lock to ensure that we don't add the 
>>> thread to the table
>>>            // that has just passed the removal point in 
>>> ThreadsSMRSupport::remove_thread()
>>>            add_thread(java_tid, thread);
>>>          }
>>>        }
>>>      }
>>>    }
>>
>> I don't like holding a completely unrelated lock, whilst holding the 
>> Threads_lock, particularly as it seems unnecessary for correctness. We 
>> have to be certain that no M&M operation that holds the Threads_lock 
>> can try to initialize the thread table. Testing can only prove the 
>> presence of that problem, not the absence.
>>
>> I think Daniil's webrev.07 version is correct and less risky than what 
>> you propose. Sorry.
>>
>> David
>> -----


More information about the serviceability-dev mailing list