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

serguei.spitsyn at oracle.com serguei.spitsyn at oracle.com
Wed Sep 18 08:01:25 UTC 2019


Hi Daniil,

On 9/17/19 17:13, Daniil Titov wrote:
> Hi Serguei,
>
> Please find below my answers to the concerns you mentioned in the previous email.
>
> 1.
>   > I have a concern about the checks for thread->is_exiting().
>   > - the lines 632-633 are useless as they do not really protect from returning an exiting thread
>> It is interesting what might happen if an exiting thread is returned by the
>> ThreadsList::find_JavaThread_from_java_tid ().
>> Does it make sense to develop a test that would cover these cases?
> I agree, it doesn't really provide any protection so it makes sense just remove it.

Now, I'm not that confident about it. :)

>   The current implementation
> find_JavaThread_from_java_tid()  doesn't provide such protection as well, since the thread could start exiting
> immediately after method find_JavaThread_from_java_tid() returns, so the assumption is that the callers of
> find_JavaThread_from_java_tid()  are expecting to deal with such threads and  looking on some of them shows that
> they usually try to retrieve threadObj or a thread statistic object and if it is NULL that just do nothing.

If I understand it correctly, the jt->threadObj() can remain non-NULL 
for some time while jt->is_exiting() == true.
It is not clear how reliable is to use it.
But this is a pre-existing issue. It is not you who introduced it. :)

So, we can skip it for now.
But for the record, we may have a source of intermittent issues.

> I'm not sure we could cover this specific case with the test. The window between find_JavaThread_from_java_tid() returns and the caller
> continues the execution is too small. The window between the thread started exiting and removed itself from the thread table is very small as well.

Understand.

> 2.
>>   - the lines 105-108 can result in adding exiting threads into the ThreadTable
>   I agree, it was missed, we need to wrap this code inside Thread_lock in the similar way as it is done find_JavaThread_from_java_tid()


Okay, thanks!

> 3.
>> I would suggest to rewrite this fragment in a safe way:
>>   95     {
>>   96       MutexLocker ml(ThreadTableCreate_lock);
>>   97       if (!_is_initialized) {
>>   98         create_table(threads->length());
>>   99         _is_initialized = true;
>> 100       }
>> 101     }
>> as:
>>        {
>>          MutexLocker ml(ThreadTableCreate_lock);
>>          if (_is_initialized) {
>>            return;
>   >        }
>   >        create_table(threads->length());
>   >        _is_initialized = true;
>   >      }
>
> It was an intension to not block  while populating the table with the threads from the current thread list.
> There is no needs to have other threads that call find_JavaThread_from_java_tid()  be blocked and waiting for
>   it to complete since the requested thread could be not present in the thread list that triggers the thread table
>   initialization. Plus in case of racing initialization it allows threads from not original  thread lists be added to the table
> and thus avoid the linear scan when these thread are looked up for the first time.


I've replied to David in another email.
Let's talk once more about it tomorrow.


> 4.
>>> The case you have described is exact the reason why we still have a code inside
>>> ThreadsList::find_JavaThread_from_java_tid() method that does a linear scan and adds
>>>    the requested thread to the thread table if it is not there ( lines 614-613 below).
>> I disagree because it is easy to avoid concurrent ThreadTable
>> initialization (please, see my separate email).
>> The reason for this code is to cover a case of late/lazy ThreadTable
>> initialization.
> David Holmes replied to this in a separate email providing a very detailed
> explanation of the possible cases and how the proposed implementation satisfies them.

Yes. Please, see above.

Thanks,
Serguei

> Best regards,
> Daniil
>
> From: "serguei.spitsyn at oracle.com" <serguei.spitsyn at oracle.com>
> Date: Tuesday, September 17, 2019 at 1:53 AM
> To: Daniil Titov <daniil.x.titov at oracle.com>, Robbin Ehn <robbin.ehn at oracle.com>, David Holmes <david.holmes at oracle.com>, <daniel.daugherty at oracle.com>, OpenJDK Serviceability <serviceability-dev at openjdk.java.net>, "hotspot-runtime-dev at openjdk.java.net" <hotspot-runtime-dev at openjdk.java.net>, "jmx-dev at openjdk.java.net" <jmx-dev at openjdk.java.net>, Claes Redestad <claes.redestad at oracle.com>
> Subject: Re: RFR: 8185005: Improve performance of ThreadMXBean.getThreadInfo(long ids[], int maxDepth)
>
> Hi Daniil,
>
> Thank you for you patience in working on this issue!
> Also, I like that the current thread related optimizations in management.cpp were factored out.
> It was a good idea to separate them.
>
> I have a concern about the checks for thread->is_exiting().
> The threads are added to and removed from the ThreadTable under protection of Threads_lock.
> However, the thread->is_exiting() checks are not protected, and so, they are racy.
>
> There is a couple of such checks to mention:
>   611 JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
>   612   ThreadTable::lazy_initialize(this);
>   613   JavaThread* thread = ThreadTable::find_thread_by_tid(java_tid);
>   614   if (thread == NULL) {
>   615     // If the thread is not found in the table find it
>   616     // with a linear search and add to the table.
>   617     for (uint i = 0; i < length(); i++) {
>   618       thread = thread_at(i);
>   619       oop tobj = thread->threadObj();
>   620       // Ignore the thread if it hasn't run yet, has exited
>   621       // or is starting to exit.
>   622       if (tobj != NULL && java_tid == java_lang_Thread::thread_id(tobj)) {
>   623         MutexLocker ml(Threads_lock);
>   624         // Must be inside the lock to ensure that we don't add the thread to the table
>   625         // that has just passed the removal point in ThreadsSMRSupport::remove_thread()
>   626         if (!thread->is_exiting()) {
>   627           ThreadTable::add_thread(java_tid, thread);
>   628           return thread;
>   629         }
>   630       }
>   631     }
>   632   } else if (!thread->is_exiting()) {
>   633       return thread;
>   634   }
>   635   return NULL;
>   636 }
>    ...
>    93 void ThreadTable::lazy_initialize(const ThreadsList *threads) {
>    94   if (!_is_initialized) {
>    95     {
>    96       MutexLocker ml(ThreadTableCreate_lock);
>    97       if (!_is_initialized) {
>    98         create_table(threads->length());
>    99         _is_initialized = true;
>   100       }
>   101     }
>   102     for (uint i = 0; i < threads->length(); i++) {
>   103       JavaThread* thread = threads->thread_at(i);
>   104       oop tobj = thread->threadObj();
>   105       if (tobj != NULL && !thread->is_exiting()) {
>   106         jlong java_tid = java_lang_Thread::thread_id(tobj);
>   107         add_thread(java_tid, thread);
>   108       }
>   109     }
>   110   }
>   111 }
>
> A thread may start exiting right after the checks at the lines 626 and 105.
> So that:
>   - the lines 632-633 are useless as they do not really protect from returning an exiting thread
>   - the lines 105-108 can result in adding exiting threads into the ThreadTable
>
> Please, note, the lines 626-629 are safe in terms of addition to the ThreadTable as they
> are protected with the Threads_lock. But the returned thread still can exit after that.
> It is interesting what might happen if an exiting thread is returned by the
> ThreadsList::find_JavaThread_from_java_tid ().
>
> Does it make sense to develop a test that would cover these cases?
>
> Thanks,
> Serguei
>
>
> On 9/16/19 11:18, Daniil Titov wrote:
> Hello,
>
> After investigating with Claes the impact of this change on the performance (thanks a lot Claes for helping with it!) the conclusion was that the impact on the thread startup time is not a blocker for this change.
>
> I also measured the memory footprint using Native Memory Tracking and results showed around 40 bytes per live thread.
>
> Please review a new version of the fix, webrev.06 [1].  Just to remind,  webrev.05 was abandoned and webrev.06 [1] is webrev.04 [3] minus changes in src/hotspot/share/services/management.cpp (that were factored out to a separate issue [4]) and plus a change in ThreadsList::find_JavaThread_from_java_tid() method (please, see below)  that addresses the problem Robbin found and puts the code that adds a new thread to the thread table inside Threads_lock.
>
> src/hotspot/share/runtime/threadSMR.cpp
>
> 622       if (tobj != NULL && java_tid == java_lang_Thread::thread_id(tobj)) {
> 623         MutexLocker ml(Threads_lock);
> 624         // Must be inside the lock to ensure that we don't add the thread to the table
> 625         // that has just passed the removal point in ThreadsSMRSupport::remove_thread()
> 626         if (!thread->is_exiting()) {
> 627           ThreadTable::add_thread(java_tid, thread);
> 628           return thread;
> 629         }
> 630       }
>
> [1] Webrev:  https://cr.openjdk.java.net/~dtitov/8185005/webrev.06
> [2] Bug: https://bugs.openjdk.java.net/browse/JDK-8185005
> [3] https://cr.openjdk.java.net/~dtitov/8185005/webrev.04
> [4] https://bugs.openjdk.java.net/browse/JDK-8229391
>
> Thank you,
> Daniil
>
>   
>
>          >
>          > On 8/4/19, 7:54 PM, "David Holmes" mailto:david.holmes at oracle.com wrote:
>          >
>          >      Hi Daniil,
>          >
>          >      On 3/08/2019 8:16 am, Daniil Titov wrote:
>          >      > Hi David,
>          >      >
>          >      > Thank you for your detailed review. Please review a new version of the fix that includes
>          >      > the changes you suggested:
>          >      > - ThreadTableCreate_lock scope is reduced to cover the creation of the table only;
>          >      > - ThreadTableCreate_lock is made _safepoint_check_always;
>          >
>          >      Okay.
>          >
>          >      > - ServiceThread is no longer responsible for the resizing of the thread table, instead,
>          >      >    the thread table is changed to grow on demand by the thread that is doing the addition;
>          >
>          >      Okay - I'm happy to get the serviceThread out of the picture here.
>          >
>          >      > - fixed nits and formatting issues.
>          >
>          >      Okay.
>          >
>          >      >>> The change also includes additional optimization for some callers of find_JavaThread_from_java_tid()
>          >      >>>   as Daniel suggested.
>          >      >> Not sure it's best to combine these, but if they are limited to the
>          >      >> changes in management.cpp only then that may be okay.
>          >      >
>          >      > The additional optimization for some callers of find_JavaThread_from_java_tid() is
>          >      > limited to management.cpp (plus a new test) so I left them in the webrev  but
>          >      > I also could move it in the separate issue if required.
>          >
>          >      I'd prefer this part of be separated out, but won't insist. Let's see if
>          >      Dan or Serguei have a strong opinion.
>          >
>          >      >    > src/hotspot/share/runtime/threadSMR.cpp
>          >      >    >755     jlong tid = SharedRuntime::get_java_tid(thread);
>          >      >    > 926     jlong tid = SharedRuntime::get_java_tid(thread);
>          >      >   >  I think it cleaner/better to just use
>          >      >   > jlong tid = java_lang_Thread::thread_id(thread->threadObj());
>          >      >   > as we know thread is not NULL, it is a JavaThread and it has to have a
>          >      >   > non-null threadObj.
>          >      >
>          >      > I had to leave this code unchanged since it turned out the threadObj is null
>          >      > when VM is destroyed:
>          >      >
>          >      > V  [libjvm.so+0xe165d7]  oopDesc::long_field(int) const+0x67
>          >      > V  [libjvm.so+0x16e06c6]  ThreadsSMRSupport::add_thread(JavaThread*)+0x116
>          >      > V  [libjvm.so+0x16d1302]  Threads::add(JavaThread*, bool)+0x82
>          >      > V  [libjvm.so+0xef8369]  attach_current_thread.part.197+0xc9
>          >      > V  [libjvm.so+0xec136c]  jni_DestroyJavaVM+0x6c
>          >      > C  [libjli.so+0x4333]  JavaMain+0x2c3
>          >      > C  [libjli.so+0x8159]  ThreadJavaMain+0x9
>          >
>          >      This is actually nothing to do with the VM being destroyed, but is an
>          >      issue with JNI_AttachCurrentThread and its interaction with the
>          >      ThreadSMR iterators. The attach process is:
>          >      - create JavaThread
>          >      - mark as "is attaching via jni"
>          >      - add to ThreadsList
>          >      - create java.lang.Thread object (you can only execute Java code after
>          >      you are attached)
>          >      - mark as "attach completed"
>          >
>          >      So while a thread "is attaching" it will be seen by the ThreadSMR thread
>          >      iterator but will have a NULL java.lang.Thread object.
>          >
>          >      We special-case attaching threads in a number of places in the VM and I
>          >      think we should be explicitly doing something here to filter out
>          >      attaching threads, rather than just being tolerant of a NULL j.l.Thread
>          >      object. Specifically in ThreadsSMRSupport::add_thread:
>          >
>          >      if (ThreadTable::is_initialized() && !thread->is_attaching_via_jni()) {
>          >         jlong tid = java_lang_Thread::thread_id(thread->threadObj());
>          >         ThreadTable::add_thread(tid, thread);
>          >      }
>          >
>          >      Note that in ThreadsSMRSupport::remove_thread we can use the same guard,
>          >      which covers the case the JNI attach encountered an error trying to
>          >      create the j.l.Thread object.
>          >
>          >      >> src/hotspot/share/services/threadTable.cpp
>          >      >> 71     static uintx get_hash(Value const& value, bool* is_dead) {
>          >      >
>          >      >> The is_dead parameter still bothers me here. I can't make enough sense
>          >      >> out of the template code in ConcurrentHashtable to see why we have to
>          >      >> have it, but I'm concerned that its very existence means we perhaps
>          >      >> should not be trying to extend CHT in this context. ??
>          >      >
>          >      > My understanding is that is_dead parameter provides a mechanism for
>          >      > ConcurrentHashtable to remove stale entries that were not explicitly
>          >      > removed by calling  ConcurrentHashTable::remove() method.
>          >      > I think that just because in our case we don't use this mechanism doesn't
>          >      > mean we should not use ConcurrentHashTable.
>          >
>          >      Can you confirm that this usage is okay with Robbin Ehn please. He's
>          >      back from vacation this week.
>          >
>          >      >> I would still want to see what impact this has on thread
>          >      >> startup cost, both with and without the table being initialized.
>          >      >
>          >      > I run a test that initializes the table by calling ThreadMXBean.get getThreadInfo(),
>          >      > starts some threads as a worm-up, and then creates and starts 100,000 threads
>          >      > (each thread just sleeps for 100 ms). In case when the thread table is enabled
>          >      > 100,000 threads are created and started  for about 15200 ms. If the thread table
>          >      > is off the test takes about 14800 ms. Based on this information the enabled
>          >      > thread table makes the thread startup about 2.7% slower.
>          >
>          >      That doesn't sound very good. I think we may need to Claes involved to
>          >      help investigate overall performance impact here.
>          >
>          >      > Webrev: https://cr.openjdk.java.net/~dtitov/8185005/webrev.04/
>          >      > Bug: https://bugs.openjdk.java.net/browse/JDK-8185005
>          >
>          >      No further code comments.
>          >
>          >      I didn't look at the test in detail.
>          >
>          >      Thanks,
>          >      David
>          >
>          >      > Thanks!
>          >      > --Daniil
>          >      >
>          >      >
>          >      > On 7/29/19, 12:53 AM, "David Holmes" mailto:david.holmes at oracle.com wrote:
>          >      >
>          >      >      Hi Daniil,
>          >      >
>          >      >      Overall I think this is a reasonable approach but I would still like to
>          >      >      see some performance and footprint numbers, both to verify it fixes the
>          >      >      problem reported, and that we are not getting penalized elsewhere.
>          >      >
>          >      >      On 25/07/2019 3:21 am, Daniil Titov wrote:
>          >      >      > Hi David, Daniel, and Serguei,
>          >      >      >
>          >      >      > Please review the new version of the fix, that makes the thread table initialization on demand and
>          >      >      > moves it inside ThreadsList::find_JavaThread_from_java_tid(). At the creation time the thread table
>          >      >      >   is initialized with the threads from the current thread list. We don't want to hold Threads_lock
>          >      >      > inside find_JavaThread_from_java_tid(),  thus new threads still could be created  while the thread
>          >      >      > table is being initialized . Such threads will be found by the linear search and added to the thread table
>          >      >      > later, in ThreadsList::find_JavaThread_from_java_tid().
>          >      >
>          >      >      The initialization allows the created but unpopulated, or partially
>          >      >      populated, table to be seen by other threads - is that your intention?
>          >      >      It seems it should be okay as the other threads will then race with the
>          >      >      initializing thread to add specific entries, and this is a concurrent
>          >      >      map so that should be functionally correct. But if so then I think you
>          >      >      can also reduce the scope of the ThreadTableCreate_lock so that it
>          >      >      covers creation of the table only, not the initial population of the table.
>          >      >
>          >      >      I like the approach of only initializing the table when needed and using
>          >      >      that to control when the add/remove-thread code needs to update the
>          >      >      table. But I would still want to see what impact this has on thread
>          >      >      startup cost, both with and without the table being initialized.
>          >      >
>          >      >      > The change also includes additional optimization for some callers of find_JavaThread_from_java_tid()
>          >      >      > as Daniel suggested.
>          >      >
>          >      >      Not sure it's best to combine these, but if they are limited to the
>          >      >      changes in management.cpp only then that may be okay. It helps to be
>          >      >      able to focus on the table related changes without being distracted by
>          >      >      other optimizations.
>          >      >
>          >      >      > That is correct that ResolvedMethodTable was used as a blueprint for the thread table, however, I tried
>          >      >      > to strip it of the all functionality that is not required in the thread table case.
>          >      >
>          >      >      The revised version seems better in that regard. But I still have a
>          >      >      concern, see below.
>          >      >
>          >      >      > We need to have the thread table resizable and allow it to grow as the number of threads increases to avoid
>          >      >      > reserving excessive memory a-priori or deteriorating lookup times. The ServiceThread is responsible for
>          >      >      > growing the thread table when required.
>          >      >
>          >      >      Yes but why? Why can't this table be grown on demand by the thread that
>          >      >      is doing the addition? For other tables we may have to delegate to the
>          >      >      service thread because the current thread cannot perform the action, or
>          >      >      it doesn't want to perform it at the time the need for the resize is
>          >      >      detected (e.g. its detected at a safepoint and you want the resize to
>          >      >      happen later outside the safepoint). It's not apparent to me that such
>          >      >      restrictions apply here.
>          >      >
>          >      >      > There is no ConcurrentHashTable available in Java 8 and for backporting this fix to Java 8 another implementation
>          >      >      > of the hash table, probably originally suggested in the patch attached to the JBS issue, should be used.  It will make
>          >      >      > the backporting more complicated,  however, adding a new Implementation of the hash table in Java 14 while it
>          >      >      > already has ConcurrentHashTable doesn't seem  reasonable for me.
>          >      >
>          >      >      Ok.
>          >      >
>          >      >      > Webrev: http://cr.openjdk.java.net/~dtitov/8185005/webrev.03
>          >      >
>          >      >      Some specific code comments:
>          >      >
>          >      >      src/hotspot/share/runtime/mutexLocker.cpp
>          >      >
>          >      >      +   def(ThreadTableCreate_lock       , PaddedMutex  , special,
>          >      >      false, Monitor::_safepoint_check_never);
>          >      >
>          >      >      I think this needs to be a _safepoint_check_always lock. The table will
>          >      >      be created by regular JavaThreads and they should (nearly) always be
>          >      >      checking for safepoints if they are going to block acquiring the lock.
>          >      >      And it isn't at all obvious that the thread doing the creation can't go
>          >      >      to a safepoint whilst this lock is held.
>          >      >
>          >      >      ---
>          >      >
>          >      >      src/hotspot/share/runtime/threadSMR.cpp
>          >      >
>          >      >      Nit:
>          >      >
>          >      >        618       JavaThread* thread = thread_at(i);
>          >      >
>          >      >      you could reuse the new java_thread local you introduced at line 613 and
>          >      >      just rename that "new" variable to "thread" so you don't have to change
>          >      >      all other uses.
>          >      >
>          >      >      628   } else if (java_thread != NULL && ...
>          >      >
>          >      >      You don't need to check != NULL here as you only get here when
>          >      >      java_thread is not NULL.
>          >      >
>          >      >        755     jlong tid = SharedRuntime::get_java_tid(thread);
>          >      >        926     jlong tid = SharedRuntime::get_java_tid(thread);
>          >      >
>          >      >      I think it cleaner/better to just use
>          >      >
>          >      >      jlong tid = java_lang_Thread::thread_id(thread->threadObj());
>          >      >
>          >      >      as we know thread is not NULL, it is a JavaThread and it has to have a
>          >      >      non-null threadObj.
>          >      >
>          >      >      ---
>          >      >
>          >      >      src/hotspot/share/services/management.cpp
>          >      >
>          >      >      1323         if (THREAD->is_Java_thread()) {
>          >      >      1324           JavaThread* current_thread = (JavaThread*)THREAD;
>          >      >
>          >      >      These calls can only be made on a JavaThread so this be simplified to
>          >      >      remove the is_Java_thread() call. Similarly in other places.
>          >      >
>          >      >      ---
>          >      >
>          >      >      src/hotspot/share/services/threadTable.cpp
>          >      >
>          >      >         55 class ThreadTableEntry : public CHeapObj<mtInternal> {
>          >      >         56   private:
>          >      >         57     jlong _tid;
>          >      >
>          >      >      I believe hotspot style is to not indent the access modifiers in C++
>          >      >      class declarations, so the above would just be:
>          >      >
>          >      >         55 class ThreadTableEntry : public CHeapObj<mtInternal> {
>          >      >         56 private:
>          >      >         57   jlong _tid;
>          >      >
>          >      >      etc.
>          >      >
>          >      >        60     ThreadTableEntry(jlong tid, JavaThread* java_thread) :
>          >      >        61     _tid(tid),_java_thread(java_thread) {}
>          >      >
>          >      >      line 61 should be indented as it continues line 60.
>          >      >
>          >      >         67 class ThreadTableConfig : public AllStatic {
>          >      >         ...
>          >      >         71     static uintx get_hash(Value const& value, bool* is_dead) {
>          >      >
>          >      >      The is_dead parameter still bothers me here. I can't make enough sense
>          >      >      out of the template code in ConcurrentHashtable to see why we have to
>          >      >      have it, but I'm concerned that its very existence means we perhaps
>          >      >      should not be trying to extend CHT in this context. ??
>          >      >
>          >      >        115   size_t start_size_log = size_log > DefaultThreadTableSizeLog
>          >      >        116   ? size_log : DefaultThreadTableSizeLog;
>          >      >
>          >      >      line 116 should be indented, though in this case I think a better layout
>          >      >      would be:
>          >      >
>          >      >        115   size_t start_size_log =
>          >      >        116       size_log > DefaultThreadTableSizeLog ? size_log :
>          >      >      DefaultThreadTableSizeLog;
>          >      >
>          >      >        131 double ThreadTable::get_load_factor() {
>          >      >        132   return (double)_items_count/_current_size;
>          >      >        133 }
>          >      >
>          >      >      Not sure that is doing what you want/expect. It will perform integer
>          >      >      division and then cast that whole integer to a double. If you want
>          >      >      double arithmetic you need:
>          >      >
>          >      >      return ((double)_items_count)/_current_size;
>          >      >
>          >      >      180     jlong          _tid;
>          >      >      181     uintx         _hash;
>          >      >
>          >      >      Nit: no need for all those spaces before the variable name.
>          >      >
>          >      >        183     ThreadTableLookup(jlong tid)
>          >      >        184     : _tid(tid), _hash(primitive_hash(tid)) {}
>          >      >
>          >      >      line 184 should be indented.
>          >      >
>          >      >      201     ThreadGet():_return(NULL) {}
>          >      >
>          >      >      Nit: need space after :
>          >      >
>          >      >        211    assert(_is_initialized, "Thread table is not initialized");
>          >      >        212   _has_work = false;
>          >      >
>          >      >      line 211 is indented one space too far.
>          >      >
>          >      >      229     ThreadTableEntry* entry = new ThreadTableEntry(tid,java_thread);
>          >      >
>          >      >      Nit: need space after ,
>          >      >
>          >      >      252   return _local_table->remove(thread,lookup);
>          >      >
>          >      >      Nit: need space after ,
>          >      >
>          >      >      Thanks,
>          >      >      David
>          >      >      ------
>          >      >
>          >      >      > Bug: https://bugs.openjdk.java.net/browse/JDK-8185005
>          >      >      >
>          >      >      > Thanks!
>          >      >      > --Daniil
>          >      >      >
>          >      >      >
>          >      >      > On 7/8/19, 3:24 PM, "Daniel D. Daugherty" mailto:daniel.daugherty at oracle.com wrote:
>          >      >      >
>          >      >      >      On 6/29/19 12:06 PM, Daniil Titov wrote:
>          >      >      >      > Hi Serguei and David,
>          >      >      >      >
>          >      >      >      > Serguei is right, ThreadTable::find_thread(java_tid) cannot  return a JavaThread with an unmatched java_tid.
>          >      >      >      >
>          >      >      >      > Please find a new version of the fix that includes the changes Serguei suggested.
>          >      >      >      >
>          >      >      >      > Regarding the concern about the maintaining the thread table when it may never even be queried, one of
>          >      >      >      > the options could be to add ThreadTable ::isEnabled flag, set it to "false" by default, and wrap the calls to the thread table
>          >      >      >      > in ThreadsSMRSupport add_thread() and remove_thread() methods to check this flag.
>          >      >      >      >
>          >      >      >      > When ThreadsList::find_JavaThread_from_java_tid() is called for the first time it could check if ThreadTable ::isEnabled
>          >      >      >      > Is on and if not then set it on and populate the thread table with all existing threads from the thread list.
>          >      >      >
>          >      >      >      I have the same concerns as David H. about this new ThreadTable.
>          >      >      >      ThreadsList::find_JavaThread_from_java_tid() is only called from code
>          >      >      >      in src/hotspot/share/services/management.cpp so I think that table
>          >      >      >      needs to enabled and populated only if it is going to be used.
>          >      >      >
>          >      >      >      I've taken a look at the webrev below and I see that David has
>          >      >      >      followed up with additional comments. Before I do a crawl through
>          >      >      >      code review for this, I would like to see the ThreadTable stuff
>          >      >      >      made optional and David's other comments addressed.
>          >      >      >
>          >      >      >      Another possible optimization is for callers of
>          >      >      >      find_JavaThread_from_java_tid() to save the calling thread's
>          >      >      >      tid value before they loop and if the current tid == saved_tid
>          >      >      >      then use the current JavaThread* instead of calling
>          >      >      >      find_JavaThread_from_java_tid() to get the JavaThread*.
>          >      >      >
>          >      >      >      Dan
>          >      >      >
>          >      >      >      >
>          >      >      >      > Webrev: https://cr.openjdk.java.net/~dtitov/8185005/webrev.02/
>          >      >      >      > Bug: https://bugs.openjdk.java.net/browse/JDK-8185005
>          >      >      >      >
>          >      >      >      > Thanks!
>          >      >      >      > --Daniil
>          >      >      >      >
>          >      >      >      > From: mailto:serguei.spitsyn at oracle.com
>          >      >      >      > Organization: Oracle Corporation
>          >      >      >      > Date: Friday, June 28, 2019 at 7:56 PM
>          >      >      >      > To: Daniil Titov mailto:daniil.x.titov at oracle.com, OpenJDK Serviceability mailto:serviceability-dev at openjdk.java.net, mailto:hotspot-runtime-dev at openjdk.java.net mailto:hotspot-runtime-dev at openjdk.java.net, mailto:jmx-dev at openjdk.java.net mailto:jmx-dev at openjdk.java.net
>          >      >      >      > Subject: Re: RFR: 8185005: Improve performance of ThreadMXBean.getThreadInfo(long ids[], int maxDepth)
>          >      >      >      >
>          >      >      >      > Hi Daniil,
>          >      >      >      >
>          >      >      >      > I have several quick comments.
>          >      >      >      >
>          >      >      >      > The indent in the hotspot c/c++ files has to be 2, not 4.
>          >      >      >      >
>          >      >      >      > https://cr.openjdk.java.net/~dtitov/8185005/webrev.01/src/hotspot/share/runtime/threadSMR.cpp.frames.html
>          >      >      >      > 614 JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
>          >      >      >      >   615     JavaThread* java_thread = ThreadTable::find_thread(java_tid);
>          >      >      >      >   616     if (java_thread == NULL && java_tid == PMIMORDIAL_JAVA_TID) {
>          >      >      >      >   617         // ThreadsSMRSupport::add_thread() is not called for the primordial
>          >      >      >      >   618         // thread. Thus, we find this thread with a linear search and add it
>          >      >      >      >   619         // to the thread table.
>          >      >      >      >   620         for (uint i = 0; i < length(); i++) {
>          >      >      >      >   621             JavaThread* thread = thread_at(i);
>          >      >      >      >   622             if (is_valid_java_thread(java_tid,thread)) {
>          >      >      >      >   623                 ThreadTable::add_thread(java_tid, thread);
>          >      >      >      >   624                 return thread;
>          >      >      >      >   625             }
>          >      >      >      >   626         }
>          >      >      >      >   627     } else if (java_thread != NULL && is_valid_java_thread(java_tid, java_thread)) {
>          >      >      >      >   628         return java_thread;
>          >      >      >      >   629     }
>          >      >      >      >   630     return NULL;
>          >      >      >      >   631 }
>          >      >      >      >   632 bool ThreadsList::is_valid_java_thread(jlong java_tid, JavaThread* java_thread) {
>          >      >      >      >   633     oop tobj = java_thread->threadObj();
>          >      >      >      >   634     // Ignore the thread if it hasn't run yet, has exited
>          >      >      >      >   635     // or is starting to exit.
>          >      >      >      >   636     return (tobj != NULL && !java_thread->is_exiting() &&
>          >      >      >      >   637             java_tid == java_lang_Thread::thread_id(tobj));
>          >      >      >      >   638 }
>          >      >      >      >
>          >      >      >      >   615     JavaThread* java_thread = ThreadTable::find_thread(java_tid);
>          >      >      >      >
>          >      >      >      >    I'd suggest to rename find_thread() to find_thread_by_tid().
>          >      >      >      >
>          >      >      >      > A space is missed after the comma:
>          >      >      >      >    622 if (is_valid_java_thread(java_tid,thread)) {
>          >      >      >      >
>          >      >      >      > An empty line is needed before L632.
>          >      >      >      >
>          >      >      >      > The name 'is_valid_java_thread' looks wrong (or confusing) to me.
>          >      >      >      > Something like 'is_alive_java_thread_with_tid()' would be better.
>          >      >      >      > It'd better to list parameters in the opposite order.
>          >      >      >      >
>          >      >      >      > The call to is_valid_java_thread() is confusing:
>          >      >      >      >     627 } else if (java_thread != NULL && is_valid_java_thread(java_tid, java_thread)) {
>          >      >      >      >
>          >      >      >      > Why would the call ThreadTable::find_thread(java_tid) return a JavaThread with an unmatched java_tid?
>          >      >      >      >
>          >      >      >      >
>          >      >      >      > Thanks,
>          >      >      >      > Serguei
>          >      >      >      >
>          >      >      >      > On 6/28/19, 9:40 PM, "David Holmes" mailto:david.holmes at oracle.com wrote:
>          >      >      >      >
>          >      >      >      >      Hi Daniil,
>          >      >      >      >
>          >      >      >      >      The definition and use of this hashtable (yet another hashtable
>          >      >      >      >      implementation!) will need careful examination. We have to be concerned
>          >      >      >      >      about the cost of maintaining it when it may never even be queried. You
>          >      >      >      >      would need to look at footprint cost and performance impact.
>          >      >      >      >
>          >      >      >      >      Unfortunately I'm just about to board a plane and will be out for the
>          >      >      >      >      next few days. I will try to look at this asap next week, but we will
>          >      >      >      >      need a lot more data on it.
>          >      >      >      >
>          >      >      >      >      Thanks,
>          >      >      >      >      David
>          >      >      >      >
>          >      >      >      > On 6/28/19 3:31 PM, Daniil Titov wrote:
>          >      >      >      > Please review the change that improves performance of ThreadMXBean MXBean methods returning the
>          >      >      >      > information for specific threads. The change introduces the thread table that uses ConcurrentHashTable
>          >      >      >      > to store one-to-one the mapping between the thread ids and JavaThread objects and replaces the linear
>          >      >      >      > search over the thread list in ThreadsList::find_JavaThread_from_java_tid(jlong tid) method with the lookup
>          >      >      >      > in the thread table.
>          >      >      >      >
>          >      >      >      > Testing: Mach5 tier1,tier2 and tier3 tests successfully passed.
>          >      >      >      >
>          >      >      >      > Webrev: https://cr.openjdk.java.net/~dtitov/8185005/webrev.01/
>          >      >      >      > Bug: https://bugs.openjdk.java.net/browse/JDK-8185005
>          >      >      >      >
>          >      >      >      > Thanks!
>          >      >      >      >
>          >      >      >      > Best regards,
>          >      >      >      > Daniil
>          >      >      >      >
>          >      >      >      >
>          >      >      >      >
>          >      >      >      >
>          >      >      >      >
>          >      >      >      >
>          >      >      >      >
>          >      >      >
>          >      >      >
>          >      >      >
>          >      >      >
>          >      >
>          >      >
>          >      >
>          >
>          >
>          >
>          
>      
>      
>      
>      
>
>
>
>
>
>



More information about the hotspot-runtime-dev mailing list