8305566: Change StringDedup thread to derive from JavaThread

Reviewed-by: stefank, cjplummer, tschatzl
This commit is contained in:
Kim Barrett 2023-04-28 03:11:00 +00:00
parent f3c90f0445
commit d3abfec8b7
18 changed files with 294 additions and 247 deletions

@ -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) {

@ -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

@ -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<ON_PHANTOM_OOP_REF>::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<ON_PHANTOM_OOP_REF>::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<true, false> 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);

@ -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<mtGC> {
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

@ -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,

@ -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;

@ -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) {

@ -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();
};

@ -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;
}

@ -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

@ -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();
}
}

@ -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();

@ -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)

@ -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) ||

@ -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));

@ -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) \
\

@ -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; }
}

@ -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() {