From d3abfec8b7ce901150952356f9f1109d09a8cb2a Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Fri, 28 Apr 2023 03:11:00 +0000 Subject: [PATCH] 8305566: Change StringDedup thread to derive from JavaThread Reviewed-by: stefank, cjplummer, tschatzl --- .../gc/shared/stringdedup/stringDedup.cpp | 33 ++--- .../gc/shared/stringdedup/stringDedup.hpp | 17 ++- .../stringdedup/stringDedupProcessor.cpp | 139 +++++++----------- .../stringdedup/stringDedupProcessor.hpp | 52 +++---- .../gc/shared/stringdedup/stringDedupStat.cpp | 67 +++------ .../gc/shared/stringdedup/stringDedupStat.hpp | 28 +--- .../shared/stringdedup/stringDedupTable.cpp | 16 +- .../shared/stringdedup/stringDedupTable.hpp | 6 +- .../shared/stringdedup/stringDedupThread.cpp | 52 +++++++ .../shared/stringdedup/stringDedupThread.hpp | 56 +++++++ .../share/gc/shenandoah/shenandoahHeap.cpp | 8 +- src/hotspot/share/runtime/java.cpp | 5 - src/hotspot/share/runtime/javaThread.hpp | 4 +- .../share/runtime/javaThread.inline.hpp | 4 + src/hotspot/share/runtime/threads.cpp | 13 +- src/hotspot/share/runtime/vmStructs.cpp | 2 + .../hotspot/runtime/StringDedupThread.java | 36 +++++ .../sun/jvm/hotspot/runtime/Threads.java | 3 +- 18 files changed, 294 insertions(+), 247 deletions(-) create mode 100644 src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp create mode 100644 src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StringDedupThread.java diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp index 91270da832b..7c37d642836 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -35,6 +35,7 @@ #include "gc/shared/stringdedup/stringDedupStat.hpp" #include "gc/shared/stringdedup/stringDedupStorageUse.hpp" #include "gc/shared/stringdedup/stringDedupTable.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/iterator.hpp" @@ -43,7 +44,6 @@ #include "oops/markWord.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/globals.hpp" -#include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.hpp" #include "runtime/safepoint.hpp" @@ -57,9 +57,12 @@ StringDedup::Processor* StringDedup::_processor = nullptr; StringDedup::Stat StringDedup::_cur_stat{}; StringDedup::Stat StringDedup::_total_stat{}; -const Klass* StringDedup::_string_klass_or_null = nullptr; -uint StringDedup::_enabled_age_threshold = 0; -uint StringDedup::_enabled_age_limit = 0; +// Configuration for predicates used to decide whether to deduplicate. +// The initial values are suitable for deduplication being disabled. +const Klass* StringDedup::_string_klass_or_null = nullptr; // No klass will match. +static_assert(markWord::max_age < UINT_MAX, "assumption"); +uint StringDedup::_enabled_age_threshold = UINT_MAX; // Age never equals max. +uint StringDedup::_enabled_age_limit = 0; // Age is never less than zero. bool StringDedup::ergo_initialize() { return Config::ergo_initialize(); @@ -82,30 +85,16 @@ void StringDedup::initialize() { _enabled_age_limit = Config::age_threshold(); Table::initialize(); Processor::initialize(); + // Don't create the thread yet. JavaThreads need to be created later. _enabled = true; log_info_p(stringdedup, init)("String Deduplication is enabled"); - } else { - // No klass will ever match. - _string_klass_or_null = nullptr; - // Age can never equal UINT_MAX. - static_assert(markWord::max_age < UINT_MAX, "assumption"); - _enabled_age_threshold = UINT_MAX; - // Age can never be less than zero. - _enabled_age_limit = 0; } _initialized = true; } -void StringDedup::stop() { +void StringDedup::start() { assert(is_enabled(), "precondition"); - assert(_processor != nullptr, "invariant"); - _processor->stop(); -} - -void StringDedup::threads_do(ThreadClosure* tc) { - assert(is_enabled(), "precondition"); - assert(_processor != nullptr, "invariant"); - tc->do_thread(_processor); + StringDedupThread::initialize(); } void StringDedup::forbid_deduplication(oop java_string) { diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp index a0b77cab654..79689ab8648 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -93,6 +93,10 @@ // but before weak reference processing, the GC should flush or delete all // of its Requests objects. // +// The deduplication thread is a daemon JavaThread. No thread visitor is +// needed, as it is handled via the normal JavaThread visiting mechanism. +// Similarly, there is no need for a stop() function. +// // For additional information on string deduplication, please see JEP 192, // https://openjdk.org/jeps/192 @@ -102,6 +106,7 @@ #include "utilities/globalDefinitions.hpp" class Klass; +class StringDedupThread; class ThreadClosure; // The StringDedup class provides the API for the deduplication mechanism. @@ -110,6 +115,8 @@ class ThreadClosure; // feature. Other functions in the StringDedup class are called where // needed, without requiring GC-specific code. class StringDedup : public AllStatic { + friend class StringDedupThread; + class Config; class Processor; class Stat; @@ -140,13 +147,9 @@ public: // Returns true if string deduplication is enabled. static bool is_enabled() { return _enabled; } - // Stop the deduplication processor thread. + // Create and start the deduplication processor thread. // precondition: is_enabled() - static void stop(); - - // Visit the deduplication processor thread. - // precondition: is_enabled() - static void threads_do(ThreadClosure* tc); + static void start(); // Marks the String as not being subject to deduplication. This can be // used to prevent deduplication of Strings whose value array must remain diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp index 744ce1d7a08..163800e9abf 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -33,21 +33,17 @@ #include "gc/shared/stringdedup/stringDedupStat.hpp" #include "gc/shared/stringdedup/stringDedupStorageUse.hpp" #include "gc/shared/stringdedup/stringDedupTable.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "oops/access.inline.hpp" #include "runtime/atomic.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" #include "utilities/globalCounter.hpp" #include "utilities/globalDefinitions.hpp" -StringDedup::Processor::Processor() : ConcurrentGCThread() { - set_name("StringDedupProcessor"); -} - OopStorage* StringDedup::Processor::_storages[2] = {}; StringDedup::StorageUse* volatile StringDedup::Processor::_storage_for_requests = nullptr; @@ -64,71 +60,63 @@ void StringDedup::Processor::initialize_storage() { _storage_for_processing = new StorageUse(_storages[1]); } +StringDedup::Processor::Processor() : _thread(nullptr) {} + void StringDedup::Processor::initialize() { _processor = new Processor(); - _processor->create_and_start(); } -bool StringDedup::Processor::wait_for_requests() const { - // Wait for the current request storage object to be non-empty. The - // num-dead notification from the Table notifies the monitor. - if (!should_terminate()) { +void StringDedup::Processor::wait_for_requests() const { + assert(Thread::current() == _thread, "precondition"); + // Wait for the current request storage object to be non-empty, or for the + // table to need cleanup. The num-dead notification from the Table notifies + // the monitor. + { + ThreadBlockInVM tbivm(_thread); MonitorLocker ml(StringDedup_lock, Mutex::_no_safepoint_check_flag); OopStorage* storage = Atomic::load(&_storage_for_requests)->storage(); - while (!should_terminate() && - (storage->allocation_count() == 0) && + while ((storage->allocation_count() == 0) && !Table::is_dead_entry_removal_needed()) { ml.wait(); } } // Swap the request and processing storage objects. - if (!should_terminate()) { - log_trace(stringdedup)("swapping request storages"); - _storage_for_processing = Atomic::xchg(&_storage_for_requests, _storage_for_processing); - GlobalCounter::write_synchronize(); - } + log_trace(stringdedup)("swapping request storages"); + _storage_for_processing = Atomic::xchg(&_storage_for_requests, _storage_for_processing); + GlobalCounter::write_synchronize(); // Wait for the now current processing storage object to no longer be used // by an in-progress GC. Again here, the num-dead notification from the // Table notifies the monitor. - if (!should_terminate()) { + { log_trace(stringdedup)("waiting for storage to process"); + ThreadBlockInVM tbivm(_thread); MonitorLocker ml(StringDedup_lock, Mutex::_no_safepoint_check_flag); - while (_storage_for_processing->is_used_acquire() && !should_terminate()) { + while (_storage_for_processing->is_used_acquire()) { ml.wait(); } } - return !should_terminate(); } StringDedup::StorageUse* StringDedup::Processor::storage_for_requests() { return StorageUse::obtain(&_storage_for_requests); } -bool StringDedup::Processor::yield_or_continue(SuspendibleThreadSetJoiner* joiner, - Stat::Phase phase) const { - if (joiner->should_yield()) { - _cur_stat.block_phase(phase); - joiner->yield(); - _cur_stat.unblock_phase(); - } - return !should_terminate(); +void StringDedup::Processor::yield() const { + assert(Thread::current() == _thread, "precondition"); + ThreadBlockInVM tbivm(_thread); } -void StringDedup::Processor::cleanup_table(SuspendibleThreadSetJoiner* joiner, - bool grow_only, - bool force) const { +void StringDedup::Processor::cleanup_table(bool grow_only, bool force) const { if (Table::cleanup_start_if_needed(grow_only, force)) { - Stat::Phase phase = Table::cleanup_phase(); - while (yield_or_continue(joiner, phase)) { - if (!Table::cleanup_step()) break; - } + do { + yield(); + } while (Table::cleanup_step()); Table::cleanup_end(); } } class StringDedup::Processor::ProcessRequest final : public OopClosure { OopStorage* _storage; - SuspendibleThreadSetJoiner* _joiner; size_t _release_index; oop* _bulk_release[OopStorage::bulk_allocate_limit]; @@ -143,9 +131,8 @@ class StringDedup::Processor::ProcessRequest final : public OopClosure { } public: - ProcessRequest(OopStorage* storage, SuspendibleThreadSetJoiner* joiner) : + ProcessRequest(OopStorage* storage) : _storage(storage), - _joiner(joiner), _release_index(0), _bulk_release() {} @@ -157,64 +144,52 @@ public: virtual void do_oop(narrowOop*) { ShouldNotReachHere(); } virtual void do_oop(oop* ref) { - if (_processor->yield_or_continue(_joiner, Stat::Phase::process)) { - oop java_string = NativeAccess::oop_load(ref); - release_ref(ref); - // Dedup java_string, after checking for various reasons to skip it. - if (java_string == nullptr) { - // String became unreachable before we got a chance to process it. - _cur_stat.inc_skipped_dead(); - } else if (java_lang_String::value(java_string) == nullptr) { - // Request during String construction, before its value array has - // been initialized. - _cur_stat.inc_skipped_incomplete(); - } else { - Table::deduplicate(java_string); - if (Table::is_grow_needed()) { - _cur_stat.report_process_pause(); - _processor->cleanup_table(_joiner, true /* grow_only */, false /* force */); - _cur_stat.report_process_resume(); - } + _processor->yield(); + oop java_string = NativeAccess::oop_load(ref); + release_ref(ref); + // Dedup java_string, after checking for various reasons to skip it. + if (java_string == nullptr) { + // String became unreachable before we got a chance to process it. + _cur_stat.inc_skipped_dead(); + } else if (java_lang_String::value(java_string) == nullptr) { + // Request during String construction, before its value array has + // been initialized. + _cur_stat.inc_skipped_incomplete(); + } else { + Table::deduplicate(java_string); + if (Table::is_grow_needed()) { + _cur_stat.report_process_pause(); + _processor->cleanup_table(true /* grow_only */, false /* force */); + _cur_stat.report_process_resume(); } } } }; -void StringDedup::Processor::process_requests(SuspendibleThreadSetJoiner* joiner) const { +void StringDedup::Processor::process_requests() const { + _cur_stat.report_process_start(); OopStorage::ParState par_state{_storage_for_processing->storage(), 1}; - ProcessRequest processor{_storage_for_processing->storage(), joiner}; + ProcessRequest processor{_storage_for_processing->storage()}; par_state.oops_do(&processor); + _cur_stat.report_process_end(); } -void StringDedup::Processor::run_service() { - while (!should_terminate()) { +void StringDedup::Processor::run(JavaThread* thread) { + assert(thread == Thread::current(), "precondition"); + _thread = thread; + log_debug(stringdedup)("Starting string deduplication thread"); + while (true) { _cur_stat.report_idle_start(); - if (!wait_for_requests()) { - assert(should_terminate(), "invariant"); - break; - } - SuspendibleThreadSetJoiner sts_joiner{}; - if (should_terminate()) break; + wait_for_requests(); _cur_stat.report_idle_end(); - _cur_stat.report_concurrent_start(); - _cur_stat.report_process_start(); - process_requests(&sts_joiner); - if (should_terminate()) break; - _cur_stat.report_process_end(); - cleanup_table(&sts_joiner, - false /* grow_only */, - StringDeduplicationResizeALot /* force */); - if (should_terminate()) break; - _cur_stat.report_concurrent_end(); + _cur_stat.report_active_start(); + process_requests(); + cleanup_table(false /* grow_only */, StringDeduplicationResizeALot /* force */); + _cur_stat.report_active_end(); log_statistics(); } } -void StringDedup::Processor::stop_service() { - MonitorLocker ml(StringDedup_lock, Mutex::_no_safepoint_check_flag); - ml.notify_all(); -} - void StringDedup::Processor::log_statistics() { _total_stat.add(&_cur_stat); Stat::log_summary(&_cur_stat, &_total_stat); diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.hpp index 07cfec73f31..3f1f1c19886 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, 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 @@ -25,26 +25,23 @@ #ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPPROCESSOR_HPP #define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPPROCESSOR_HPP -#include "gc/shared/concurrentGCThread.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" -#include "gc/shared/stringdedup/stringDedupStat.hpp" +#include "memory/allocation.hpp" #include "utilities/macros.hpp" +class JavaThread; class OopStorage; -class SuspendibleThreadSetJoiner; -// Thread class for string deduplication. There is only one instance of -// this class. This thread processes deduplication requests. It also -// manages the deduplication table, performing resize and cleanup operations -// as needed. This includes managing the OopStorage objects used to hold -// requests. +// This class performs string deduplication. There is only one instance of +// this class. It processes deduplication requests. It also manages the +// deduplication table, performing resize and cleanup operations as needed. +// This includes managing the OopStorage objects used to hold requests. // -// This thread uses the SuspendibleThreadSet mechanism to take part in the -// safepoint protocol. It checks for safepoints between processing requests -// in order to minimize safepoint latency. The Table provides incremental -// operations for resizing and for removing dead entries, so this thread can -// perform safepoint checks between steps in those operations. -class StringDedup::Processor : public ConcurrentGCThread { +// Processing periodically checks for and yields at safepoints. Processing of +// requests is performed in incremental chunks. The Table provides +// incremental operations for resizing and for removing dead entries, so +// safepoint checks can be performed between steps in those operations. +class StringDedup::Processor : public CHeapObj { Processor(); ~Processor() = default; @@ -54,27 +51,32 @@ class StringDedup::Processor : public ConcurrentGCThread { static StorageUse* volatile _storage_for_requests; static StorageUse* _storage_for_processing; - // Returns !should_terminate(); - bool wait_for_requests() const; + JavaThread* _thread; - // Yield if requested. Returns !should_terminate() after possible yield. - bool yield_or_continue(SuspendibleThreadSetJoiner* joiner, Stat::Phase phase) const; + // Wait until there are requests to be processed. The storage for requests + // and storage for processing are swapped; the former requests storage + // becomes the current processing storage, and vice versa. + // precondition: the processing storage is empty. + void wait_for_requests() const; + + // Yield if requested. + void yield() const; class ProcessRequest; - void process_requests(SuspendibleThreadSetJoiner* joiner) const; - void cleanup_table(SuspendibleThreadSetJoiner* joiner, bool grow_only, bool force) const; + void process_requests() const; + void cleanup_table(bool grow_only, bool force) const; void log_statistics(); -protected: - virtual void run_service(); - virtual void stop_service(); - public: static void initialize(); static void initialize_storage(); static StorageUse* storage_for_requests(); + + // Use thread as the deduplication thread. + // precondition: thread == Thread::current() + void run(JavaThread* thread); }; #endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPPROCESSOR_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp index ee37083ee42..cffda333b13 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -40,21 +40,19 @@ StringDedup::Stat::Stat() : _skipped_dead(0), _skipped_incomplete(0), _skipped_shared(0), - _concurrent(0), + _active(0), _idle(0), _process(0), _resize_table(0), _cleanup_table(0), - _block(0), - _concurrent_start(), - _concurrent_elapsed(), + _active_start(), + _active_elapsed(), _phase_start(), _idle_elapsed(), _process_elapsed(), _resize_table_elapsed(), - _cleanup_table_elapsed(), - _block_elapsed() { -} + _cleanup_table_elapsed() +{} void StringDedup::Stat::add(const Stat* const stat) { _inspected += stat->_inspected; @@ -69,18 +67,16 @@ void StringDedup::Stat::add(const Stat* const stat) { _skipped_dead += stat->_skipped_dead; _skipped_incomplete += stat->_skipped_incomplete; _skipped_shared += stat->_skipped_shared; - _concurrent += stat->_concurrent; + _active += stat->_active; _idle += stat->_idle; _process += stat->_process; _resize_table += stat->_resize_table; _cleanup_table += stat->_cleanup_table; - _block += stat->_block; - _concurrent_elapsed += stat->_concurrent_elapsed; + _active_elapsed += stat->_active_elapsed; _idle_elapsed += stat->_idle_elapsed; _process_elapsed += stat->_process_elapsed; _resize_table_elapsed += stat->_resize_table_elapsed; _cleanup_table_elapsed += stat->_cleanup_table_elapsed; - _block_elapsed += stat->_block_elapsed; } // Support for log output formatting @@ -113,19 +109,19 @@ void StringDedup::Stat::log_summary(const Stat* last_stat, const Stat* total_sta last_stat->_deduped, STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes), total_deduped_bytes_percent, strdedup_elapsed_param_ms(last_stat->_process_elapsed), - strdedup_elapsed_param_ms(last_stat->_concurrent_elapsed)); + strdedup_elapsed_param_ms(last_stat->_active_elapsed)); } -void StringDedup::Stat::report_concurrent_start() { - log_debug(stringdedup, phases, start)("Concurrent start"); - _concurrent_start = Ticks::now(); - _concurrent++; +void StringDedup::Stat::report_active_start() { + log_debug(stringdedup, phases, start)("Active start"); + _active_start = Ticks::now(); + _active++; } -void StringDedup::Stat::report_concurrent_end() { - _concurrent_elapsed += (Ticks::now() - _concurrent_start); - log_debug(stringdedup, phases)("Concurrent end: " STRDEDUP_ELAPSED_FORMAT_MS, - strdedup_elapsed_param_ms(_concurrent_elapsed)); +void StringDedup::Stat::report_active_end() { + _active_elapsed += (Ticks::now() - _active_start); + log_debug(stringdedup, phases)("Active end: " STRDEDUP_ELAPSED_FORMAT_MS, + strdedup_elapsed_param_ms(_active_elapsed)); } void StringDedup::Stat::report_phase_start(const char* phase) { @@ -194,38 +190,13 @@ void StringDedup::Stat::report_cleanup_table_end() { report_phase_end("Cleanup Table", &_cleanup_table_elapsed); } -Tickspan* StringDedup::Stat::elapsed_for_phase(Phase phase) { - switch (phase) { - case Phase::process: return &_process_elapsed; - case Phase::resize_table: return &_resize_table_elapsed; - case Phase::cleanup_table: return &_cleanup_table_elapsed; - } - ShouldNotReachHere(); - return nullptr; -} - -void StringDedup::Stat::block_phase(Phase phase) { - Ticks now = Ticks::now(); - *elapsed_for_phase(phase) += now - _phase_start; - _phase_start = now; - _block++; -} - -void StringDedup::Stat::unblock_phase() { - Ticks now = Ticks::now(); - _block_elapsed += now - _phase_start; - _phase_start = now; -} - void StringDedup::Stat::log_times(const char* prefix) const { log_debug(stringdedup)( " %s Process: %zu/" STRDEDUP_ELAPSED_FORMAT_MS - ", Idle: %zu/" STRDEDUP_ELAPSED_FORMAT_MS - ", Blocked: %zu/" STRDEDUP_ELAPSED_FORMAT_MS, + ", Idle: %zu/" STRDEDUP_ELAPSED_FORMAT_MS, prefix, _process, strdedup_elapsed_param_ms(_process_elapsed), - _idle, strdedup_elapsed_param_ms(_idle_elapsed), - _block, strdedup_elapsed_param_ms(_block_elapsed)); + _idle, strdedup_elapsed_param_ms(_idle_elapsed)); if (_resize_table > 0) { log_debug(stringdedup)( " %s Resize Table: %zu/" STRDEDUP_ELAPSED_FORMAT_MS, diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp index 546578b96a1..db753af3be5 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -34,14 +34,6 @@ // Operation counters are updated when deduplicating a string. // Phase timing information is collected by the processing thread. class StringDedup::Stat { -public: - // Only phases that can be blocked, so excluding "idle". - enum class Phase { - process, - resize_table, - cleanup_table - }; - private: // Counters size_t _inspected; @@ -58,26 +50,25 @@ private: size_t _skipped_shared; // Phase counters for deduplication thread - size_t _concurrent; + size_t _active; size_t _idle; size_t _process; size_t _resize_table; size_t _cleanup_table; - size_t _block; // Time spent by the deduplication thread in different phases - Ticks _concurrent_start; - Tickspan _concurrent_elapsed; + Ticks _active_start; + Tickspan _active_elapsed; Ticks _phase_start; + // These phases are disjoint, so share _phase_start. + // Some of these overlap with active, hence need _active_start. Tickspan _idle_elapsed; Tickspan _process_elapsed; Tickspan _resize_table_elapsed; Tickspan _cleanup_table_elapsed; - Tickspan _block_elapsed; void report_phase_start(const char* phase); void report_phase_end(const char* phase, Tickspan* elapsed); - Tickspan* elapsed_for_phase(Phase phase); void log_times(const char* prefix) const; @@ -153,11 +144,8 @@ public: void report_cleanup_table_start(size_t entry_count, size_t dead_count); void report_cleanup_table_end(); - void report_concurrent_start(); - void report_concurrent_end(); - - void block_phase(Phase phase); - void unblock_phase(); + void report_active_start(); + void report_active_end(); void add(const Stat* const stat); void log_statistics(bool total) const; diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 28a0872fb29..ac8fc0759ca 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -283,7 +283,6 @@ public: virtual bool step() = 0; virtual TableValue find(typeArrayOop obj, uint hash_code) const = 0; virtual void report_end() const = 0; - virtual Stat::Phase phase() const = 0; virtual void verify() const = 0; }; @@ -321,10 +320,6 @@ public: _cur_stat.report_resize_table_end(); } - virtual Stat::Phase phase() const { - return Stat::Phase::resize_table; - } - virtual void verify() const; }; @@ -389,10 +384,6 @@ public: _cur_stat.report_cleanup_table_end(); } - virtual Stat::Phase phase() const { - return Stat::Phase::cleanup_table; - } - virtual void verify() const {} // Nothing to do here. }; @@ -718,11 +709,6 @@ void StringDedup::Table::cleanup_end() { Atomic::store(&_dead_state, DeadState::wait2); } -StringDedup::Stat::Phase StringDedup::Table::cleanup_phase() { - assert(_cleanup_state != nullptr, "precondition"); - return _cleanup_state->phase(); -} - void StringDedup::Table::verify() { size_t total_count = 0; for (size_t i = 0; i < _number_of_buckets; ++i) { diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp index ed2d379aedd..19df8184a05 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -138,10 +138,6 @@ public: // precondition: a cleanup is in progress. static void cleanup_end(); - // Return the phase kind for the cleanup being performed. - // precondition: a cleanup is in progress. - static Stat::Phase cleanup_phase(); - static void verify(); static void log_statistics(); }; diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp new file mode 100644 index 00000000000..371b409abfd --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/stringdedup/stringDedupProcessor.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" +#include "runtime/handles.hpp" +#include "runtime/os.hpp" +#include "utilities/exceptions.hpp" + +StringDedupThread::StringDedupThread() : JavaThread(thread_entry) {} + +void StringDedupThread::initialize() { + EXCEPTION_MARK; + + const char* name = "StringDedupThread"; + Handle thread_oop = JavaThread::create_system_thread_object(name, CHECK); + StringDedupThread* thread = new StringDedupThread(); + JavaThread::vm_exit_on_osthread_failure(thread); + JavaThread::start_internal_daemon(THREAD, thread, thread_oop, NormPriority); +} + +void StringDedupThread::thread_entry(JavaThread* thread, TRAPS) { + StringDedup::_processor->run(thread); +} + +bool StringDedupThread::is_hidden_from_external_view() const { + return true; +} + diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp new file mode 100644 index 00000000000..504e1fe617f --- /dev/null +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, 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_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP +#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP + +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "runtime/javaThread.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/macros.hpp" + +// Thread class for string deduplication. There is only one instance of this +// class. This class provides thread management. It uses the Processor +// to perform most of the work. +// +// Unlike most of the classes in the stringdedup implementation, this class is +// not an inner class of StringDedup. This is because we need a simple public +// identifier for use by VMStructs. +class StringDedupThread : public JavaThread { + friend class VMStructs; + + StringDedupThread(); + ~StringDedupThread() = default; + + NONCOPYABLE(StringDedupThread); + + static void thread_entry(JavaThread* thread, TRAPS); + +public: + static void initialize(); + + bool is_hidden_from_external_view() const override; +}; + +#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index b213625c80e..382e037a7fa 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -58,7 +58,6 @@ #include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" -#include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" @@ -1186,9 +1185,6 @@ void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const { if (_safepoint_workers != nullptr) { _safepoint_workers->threads_do(tcl); } - if (ShenandoahStringDedup::is_enabled()) { - ShenandoahStringDedup::threads_do(tcl); - } } void ShenandoahHeap::print_tracing_info() const { @@ -2193,13 +2189,13 @@ bool ShenandoahHeap::uncommit_bitmap_slice(ShenandoahHeapRegion *r) { } void ShenandoahHeap::safepoint_synchronize_begin() { - if (ShenandoahSuspendibleWorkers || UseStringDeduplication) { + if (ShenandoahSuspendibleWorkers) { SuspendibleThreadSet::synchronize(); } } void ShenandoahHeap::safepoint_synchronize_end() { - if (ShenandoahSuspendibleWorkers || UseStringDeduplication) { + if (ShenandoahSuspendibleWorkers) { SuspendibleThreadSet::desynchronize(); } } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 44c82a0a849..140d0281e2f 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -479,11 +479,6 @@ void before_exit(JavaThread* thread, bool halt) { StatSampler::disengage(); StatSampler::destroy(); - // Shut down string deduplication if running. - if (StringDedup::is_enabled()) { - StringDedup::stop(); - } - // Stop concurrent GC threads Universe::heap()->stop(); diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 14c6b39a779..795a59b3ccb 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -512,9 +512,7 @@ private: virtual bool is_Java_thread() const { return true; } virtual bool can_call_java() const { return true; } - virtual bool is_active_Java_thread() const { - return on_thread_list() && !is_terminated(); - } + virtual bool is_active_Java_thread() const; // Thread oop. threadObj() can be null for initial JavaThread // (or for threads attached via JNI) diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index 4e4399d77a4..7b1ad7e17e1 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -223,6 +223,10 @@ inline void JavaThread::set_terminated(TerminatedTypes t) { Atomic::release_store(&_terminated, t); } +inline bool JavaThread::is_active_Java_thread() const { + return on_thread_list() && !is_terminated(); +} + // Allow tracking of class initialization monitor use inline void JavaThread::set_class_to_be_initialized(InstanceKlass* k) { assert((k == nullptr && _class_to_be_initialized != nullptr) || diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 384936b905e..0517150feed 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -691,6 +691,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { } #endif + // Start string deduplication thread if requested. + if (StringDedup::is_enabled()) { + StringDedup::start(); + } + // Pre-initialize some JSR292 core classes to avoid deadlock during class loading. // It is done after compilers are initialized, because otherwise compilations of // signature polymorphic MH intrinsics can be missed @@ -1267,9 +1272,6 @@ void Threads::print_on(outputStream* st, bool print_stacks, PrintOnClosure cl(st); cl.do_thread(VMThread::vm_thread()); Universe::heap()->gc_threads_do(&cl); - if (StringDedup::is_enabled()) { - StringDedup::threads_do(&cl); - } cl.do_thread(WatcherThread::watcher_thread()); cl.do_thread(AsyncLogWriter::instance()); @@ -1332,11 +1334,6 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf, Universe::heap()->gc_threads_do(&print_closure); } - if (StringDedup::is_enabled()) { - PrintOnErrorClosure print_closure(st, current, buf, buflen, &found_current); - StringDedup::threads_do(&print_closure); - } - if (!found_current) { st->cr(); st->print("=>" PTR_FORMAT " (exited) ", p2i(current)); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 0aea64ba14a..05fd3d2c6e0 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -48,6 +48,7 @@ #include "code/vmreg.hpp" #include "compiler/compileBroker.hpp" #include "compiler/oopMap.hpp" +#include "gc/shared/stringdedup/stringDedupThread.hpp" #include "gc/shared/vmStructs_gc.hpp" #include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" @@ -1311,6 +1312,7 @@ declare_type(ServiceThread, JavaThread) \ declare_type(NotificationThread, JavaThread) \ declare_type(CompilerThread, JavaThread) \ + declare_type(StringDedupThread, JavaThread) \ declare_toplevel_type(OSThread) \ declare_toplevel_type(JavaFrameAnchor) \ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StringDedupThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StringDedupThread.java new file mode 100644 index 00000000000..3922a2275da --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StringDedupThread.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023, 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. + * + */ + +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.Address; + +public class StringDedupThread extends JavaThread { + public StringDedupThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + public boolean isHiddenFromExternalView() { return true; } +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java index 451d1423a16..87ee52c7c3e 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, 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 @@ -156,6 +156,7 @@ public class Threads { virtualConstructor.addMapping("ServiceThread", ServiceThread.class); virtualConstructor.addMapping("MonitorDeflationThread", MonitorDeflationThread.class); virtualConstructor.addMapping("NotificationThread", NotificationThread.class); + virtualConstructor.addMapping("StringDedupThread", StringDedupThread.class); } public Threads() {