RFR: 8284632: runtime/Thread/StopAtExit.java possibly leaking memory again

Daniel D.Daugherty dcubed at openjdk.java.net
Wed Apr 27 21:40:46 UTC 2022


On Wed, 27 Apr 2022 17:24:16 GMT, Daniel D. Daugherty <dcubed at openjdk.org> wrote:

>> src/hotspot/share/runtime/handshake.cpp line 325:
>> 
>>> 323:     if (_handshake_cl->is_async_installer()) {
>>> 324:       _handshake_cl->do_cleanup();
>>> 325:     }
>> 
>> Rather than defining this is_async_installer() I would just add a is_exiting() check in JavaThread::install_async_exception(), like we do with suspend_with_handshake(), so we never install an async exception if the JT already moved to the _thread_exiting state. This !thread->is_terminated() check still succeeds if the thread moved to the _thread_exiting state, so we could still install an async exception between the JT moving to _thread_exiting and moving to _thread_terminated (the JT might block when trying to grab the Threads_lock to remove itself from the thread list). So in that case, we would still leak memory. (Maybe we should just check for _not_terminated here, not sure why we allow executing the handshake closure when thread is already exiting).
>> 
>> To avoid future issues I would define ~HandshakeState() with a single debug line: assert(!has_operation(), "leaking memory, queue should be empty"). If you add the following code to StopAtExit.java this assert will fire do to what I mentioned about the !thread->is_terminated() check still succeeding between _thread_exiting and _thread_terminated:
>> 
>> 
>> // Fire-up thread that just creates new threads contending on Threads_lock
>> Thread threadCreator = new Thread() {
>>     @Override
>>     public void run() {
>>         while (true) {
>>             Thread dummyThread = new Thread(() -> {});
>>             dummyThread.start();
>>             try {
>>                 dummyThread.join();
>>             } catch(InterruptedException ie) {
>>             }
>>         }
>>     }
>> };
>> threadCreator.setDaemon(true);
>> threadCreator.start();
>
> @pchilano - Thanks for reviewing! I'll have to mull on your feedback and
> then, of course, there will a long stress test cycle needed to verify.

It appears that adding an `is_exiting()` check to `JavaThread::install_async_exception()`
is insufficient. I restored my debug counter code and disabled the two leak fixes in this
PR and retested:


vm_exit: dcubed_async_global_alloc_cnt=936344
vm_exit: dcubed_drop_installer_thread_terminated_cnt=113472
vm_exit: dcubed_drop_is_exiting_cnt=13
vm_exit: dcubed_async_blocked_cnt=67577
dcubed_async_global_release_cnt=755282


dcubed_async_global_alloc_cnt=936344 - dcubed_async_global_release_cnt=755282
181062 // The number of exceptions leaked.

181062 - dcubed_drop_is_exiting_cnt=13
181049 // The number of leaked exceptions that made it past the is_exiting() check.

181049 - dcubed_drop_installer_thread_terminated_cnt=113472
67577 // The number of exceptions that made it past the is_exiting() check and
           // the is_terminated() check in HandshakeOperation::do_handshake().

67577 - dcubed_async_blocked_cnt=67577
0   // The number of exception that made it past the cleanup in JavaThread::exit

-------------

PR: https://git.openjdk.java.net/jdk/pull/8388


More information about the hotspot-runtime-dev mailing list