8254598: StringDedupTable should use OopStorage

Co-authored-by: Kim Barrett <kbarrett@openjdk.org>
Co-authored-by: Zhengyu Gu <zgu@openjdk.org>
Reviewed-by: coleenp, iklam, tschatzl, ayang
This commit is contained in:
Kim Barrett 2021-05-14 18:38:58 +00:00
parent 360928d16d
commit be0a655208
97 changed files with 2399 additions and 3234 deletions

View File

@ -1375,21 +1375,6 @@ class CountSharedSymbols : public SymbolClosure {
};
// For -XX:PrintSharedArchiveAndExit
class CountSharedStrings : public OopClosure {
private:
int _count;
public:
CountSharedStrings() : _count(0) {}
void do_oop(oop* p) {
_count++;
}
void do_oop(narrowOop* p) {
_count++;
}
int total() { return _count; }
};
// Read the miscellaneous data from the shared file, and
// serialize it out to its various destinations.
@ -1444,9 +1429,7 @@ void MetaspaceShared::initialize_shared_spaces() {
CountSharedSymbols cl;
SymbolTable::shared_symbols_do(&cl);
tty->print_cr("Number of shared symbols: %d", cl.total());
CountSharedStrings cs;
StringTable::shared_oops_do(&cs);
tty->print_cr("Number of shared strings: %d", cs.total());
tty->print_cr("Number of shared strings: %zu", StringTable::shared_entry_count());
tty->print_cr("VM version: %s\r\n", static_mapinfo->vm_version());
if (FileMapInfo::current_info() == NULL || _archive_loading_failed) {
tty->print_cr("archive is invalid");

View File

@ -222,10 +222,14 @@ public:
// Read/Write the table's header from/to the CDS archive
void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN;
inline bool empty() {
inline bool empty() const {
return (_entry_count == 0);
}
inline size_t entry_count() const {
return _entry_count;
}
static size_t calculate_header_size();
};

View File

@ -201,6 +201,7 @@ int java_lang_String::_value_offset;
int java_lang_String::_hash_offset;
int java_lang_String::_hashIsZero_offset;
int java_lang_String::_coder_offset;
int java_lang_String::_flags_offset;
bool java_lang_String::_initialized;
@ -208,6 +209,18 @@ bool java_lang_String::is_instance(oop obj) {
return is_instance_inlined(obj);
}
bool java_lang_String::test_and_set_flag(oop java_string, uint8_t flag_mask) {
uint8_t* addr = flags_addr(java_string);
uint8_t value = Atomic::load(addr);
while ((value & flag_mask) == 0) {
uint8_t old_value = value;
value |= flag_mask;
value = Atomic::cmpxchg(addr, old_value, value);
if (value == old_value) return false; // Flag bit changed from 0 to 1.
}
return true; // Flag bit is already 1.
}
#define STRING_FIELDS_DO(macro) \
macro(_value_offset, k, vmSymbols::value_name(), byte_array_signature, false); \
macro(_hash_offset, k, "hash", int_signature, false); \
@ -221,6 +234,7 @@ void java_lang_String::compute_offsets() {
InstanceKlass* k = vmClasses::String_klass();
STRING_FIELDS_DO(FIELD_COMPUTE_OFFSET);
STRING_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
_initialized = true;
}
@ -228,6 +242,7 @@ void java_lang_String::compute_offsets() {
#if INCLUDE_CDS
void java_lang_String::serialize_offsets(SerializeClosure* f) {
STRING_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
STRING_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET);
f->do_bool(&_initialized);
}
#endif

View File

@ -96,12 +96,18 @@ class java_lang_Object : AllStatic {
// Interface to java.lang.String objects
// The flags field is a collection of bits representing boolean values used
// internally by the VM.
#define STRING_INJECTED_FIELDS(macro) \
macro(java_lang_String, flags, byte_signature, false)
class java_lang_String : AllStatic {
private:
static int _value_offset;
static int _hash_offset;
static int _hashIsZero_offset;
static int _coder_offset;
static int _flags_offset;
static bool _initialized;
@ -109,6 +115,19 @@ class java_lang_String : AllStatic {
static inline void set_coder(oop string, jbyte coder);
// Bitmasks for values in the injected flags field.
static const uint8_t _deduplication_forbidden_mask = 1 << 0;
static const uint8_t _deduplication_requested_mask = 1 << 1;
static int flags_offset() { CHECK_INIT(_flags_offset); }
// Return the address of the injected flags field.
static inline uint8_t* flags_addr(oop java_string);
// Test whether the designated bit of the injected flags field is set.
static inline bool is_flag_set(oop java_string, uint8_t flag_mask);
// Atomically test and set the designated bit of the injected flags field,
// returning true if the bit was already set.
static bool test_and_set_flag(oop java_string, uint8_t flag_mask);
public:
// Coders
@ -137,11 +156,26 @@ class java_lang_String : AllStatic {
static inline void set_value_raw(oop string, typeArrayOop buffer);
static inline void set_value(oop string, typeArrayOop buffer);
// Set the deduplication_forbidden flag true. This flag is sticky; once
// set it never gets cleared. This is set when a String is interned in
// the StringTable, to prevent string deduplication from changing the
// String's value array.
static inline void set_deduplication_forbidden(oop java_string);
// Test and set the deduplication_requested flag. Returns the old value
// of the flag. This flag is sticky; once set it never gets cleared.
// Some GCs may use this flag when deciding whether to request
// deduplication of a String, to avoid multiple requests for the same
// object.
static inline bool test_and_set_deduplication_requested(oop java_string);
// Accessors
static inline typeArrayOop value(oop java_string);
static inline typeArrayOop value_no_keepalive(oop java_string);
static inline bool hash_is_set(oop string);
static inline bool is_latin1(oop java_string);
static inline bool deduplication_forbidden(oop java_string);
static inline bool deduplication_requested(oop java_string);
static inline int length(oop java_string);
static inline int length(oop java_string, typeArrayOop string_value);
static int utf8_length(oop java_string);
@ -1735,6 +1769,7 @@ class InjectedField {
klass##_##name##_enum,
#define ALL_INJECTED_FIELDS(macro) \
STRING_INJECTED_FIELDS(macro) \
CLASS_INJECTED_FIELDS(macro) \
CLASSLOADER_INJECTED_FIELDS(macro) \
RESOLVEDMETHOD_INJECTED_FIELDS(macro) \

View File

@ -73,6 +73,32 @@ bool java_lang_String::is_latin1(oop java_string) {
return coder == CODER_LATIN1;
}
uint8_t* java_lang_String::flags_addr(oop java_string) {
assert(_initialized, "Must be initialized");
assert(is_instance(java_string), "Must be java string");
return java_string->obj_field_addr<uint8_t>(_flags_offset);
}
bool java_lang_String::is_flag_set(oop java_string, uint8_t flag_mask) {
return (Atomic::load(flags_addr(java_string)) & flag_mask) != 0;
}
bool java_lang_String::deduplication_forbidden(oop java_string) {
return is_flag_set(java_string, _deduplication_forbidden_mask);
}
bool java_lang_String::deduplication_requested(oop java_string) {
return is_flag_set(java_string, _deduplication_requested_mask);
}
void java_lang_String::set_deduplication_forbidden(oop java_string) {
test_and_set_flag(java_string, _deduplication_forbidden_mask);
}
bool java_lang_String::test_and_set_deduplication_requested(oop java_string) {
return test_and_set_flag(java_string, _deduplication_requested_mask);
}
int java_lang_String::length(oop java_string, typeArrayOop value) {
assert(_initialized, "Must be initialized");
assert(is_instance(java_string), "must be java_string");

View File

@ -33,11 +33,11 @@
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/oopStorage.inline.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/access.inline.hpp"
#include "oops/compressedOops.hpp"
#include "oops/oop.inline.hpp"
@ -346,15 +346,17 @@ oop StringTable::do_intern(Handle string_or_null_h, const jchar* name,
string_h = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
}
// Deduplicate the string before it is interned. Note that we should never
// deduplicate a string after it has been interned. Doing so will counteract
// compiler optimizations done on e.g. interned string literals.
Universe::heap()->deduplicate_string(string_h());
assert(java_lang_String::equals(string_h(), name, len),
"string must be properly initialized");
assert(len == java_lang_String::length(string_h()), "Must be same length");
// Notify deduplication support that the string is being interned. A string
// must never be deduplicated after it has been interned. Doing so interferes
// with compiler optimizations done on e.g. interned string literals.
if (StringDedup::is_enabled()) {
StringDedup::notify_intern(string_h());
}
StringTableLookupOop lookup(THREAD, hash, string_h);
StringTableGet stg(THREAD);
@ -700,12 +702,20 @@ void StringtableDCmd::execute(DCmdSource source, TRAPS) {
// Sharing
#if INCLUDE_CDS_JAVA_HEAP
size_t StringTable::shared_entry_count() {
return _shared_table.entry_count();
}
oop StringTable::lookup_shared(const jchar* name, int len, unsigned int hash) {
assert(hash == java_lang_String::hash_code(name, len),
"hash must be computed using java_lang_String::hash_code");
return _shared_table.lookup(name, hash, len);
}
oop StringTable::lookup_shared(const jchar* name, int len) {
return _shared_table.lookup(name, java_lang_String::hash_code(name, len), len);
}
oop StringTable::create_archived_string(oop s) {
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
assert(java_lang_String::is_instance(s), "sanity");
@ -724,6 +734,10 @@ oop StringTable::create_archived_string(oop s) {
// adjust the pointer to the 'value' field in the new String oop
java_lang_String::set_value_raw(new_s, new_v);
// Prevent string deduplication from changing the 'value' field to
// something not in the archive before building the archive. Also marks
// the shared string when loaded.
java_lang_String::set_deduplication_forbidden(new_s);
return new_s;
}
@ -769,17 +783,4 @@ void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
}
}
class SharedStringIterator {
OopClosure* _oop_closure;
public:
SharedStringIterator(OopClosure* f) : _oop_closure(f) {}
void do_value(oop string) {
_oop_closure->do_oop(&string);
}
};
void StringTable::shared_oops_do(OopClosure* f) {
SharedStringIterator iter(f);
_shared_table.iterate(&iter);
}
#endif //INCLUDE_CDS_JAVA_HEAP

View File

@ -107,8 +107,9 @@ class StringTable : public CHeapObj<mtSymbol>{
private:
static oop lookup_shared(const jchar* name, int len, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
public:
static oop lookup_shared(const jchar* name, int len) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
static size_t shared_entry_count() NOT_CDS_JAVA_HEAP_RETURN_(0);
static oop create_archived_string(oop s) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
static void shared_oops_do(OopClosure* f) NOT_CDS_JAVA_HEAP_RETURN;
static void write_to_archive(const DumpedInternedStrings* dumped_interned_strings) NOT_CDS_JAVA_HEAP_RETURN;
static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;

View File

@ -61,7 +61,6 @@
#include "gc/g1/g1RootClosures.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1SATBMarkQueueSet.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
#include "gc/g1/g1Trace.hpp"
#include "gc/g1/g1ServiceThread.hpp"
@ -1706,8 +1705,6 @@ jint G1CollectedHeap::initialize() {
// values in the heap have been properly initialized.
_g1mm = new G1MonitoringSupport(this);
G1StringDedup::initialize();
_preserved_marks_set.init(ParallelGCThreads);
_collection_set.initialize(max_reserved_regions());
@ -1724,9 +1721,6 @@ void G1CollectedHeap::stop() {
_cr->stop();
_service_thread->stop();
_cm_thread->stop();
if (G1StringDedup::is_enabled()) {
G1StringDedup::stop();
}
}
void G1CollectedHeap::safepoint_synchronize_begin() {
@ -2309,14 +2303,6 @@ size_t G1CollectedHeap::max_capacity() const {
return max_regions() * HeapRegion::GrainBytes;
}
void G1CollectedHeap::deduplicate_string(oop str) {
assert(java_lang_String::is_instance(str), "invariant");
if (G1StringDedup::is_enabled()) {
G1StringDedup::deduplicate(str);
}
}
void G1CollectedHeap::prepare_for_verify() {
_verifier->prepare_for_verify();
}
@ -2437,9 +2423,6 @@ void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const {
_cm->threads_do(tc);
_cr->threads_do(tc);
tc->do_thread(_service_thread);
if (G1StringDedup::is_enabled()) {
G1StringDedup::threads_do(tc);
}
}
void G1CollectedHeap::print_tracing_info() const {
@ -3089,55 +3072,10 @@ void G1ParEvacuateFollowersClosure::do_void() {
void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive,
bool class_unloading_occurred) {
uint num_workers = workers()->active_workers();
G1ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
G1ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred);
workers()->run_task(&unlink_task);
}
// Clean string dedup data structures.
// Ideally we would prefer to use a StringDedupCleaningTask here, but we want to
// record the durations of the phases. Hence the almost-copy.
class G1StringDedupCleaningTask : public AbstractGangTask {
BoolObjectClosure* _is_alive;
OopClosure* _keep_alive;
G1GCPhaseTimes* _phase_times;
public:
G1StringDedupCleaningTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
G1GCPhaseTimes* phase_times) :
AbstractGangTask("Partial Cleaning Task"),
_is_alive(is_alive),
_keep_alive(keep_alive),
_phase_times(phase_times)
{
assert(G1StringDedup::is_enabled(), "String deduplication disabled.");
StringDedup::gc_prologue(true);
}
~G1StringDedupCleaningTask() {
StringDedup::gc_epilogue();
}
void work(uint worker_id) {
StringDedupUnlinkOrOopsDoClosure cl(_is_alive, _keep_alive);
{
G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupQueueFixup, worker_id);
StringDedupQueue::unlink_or_oops_do(&cl);
}
{
G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupTableFixup, worker_id);
StringDedupTable::unlink_or_oops_do(&cl, worker_id);
}
}
};
void G1CollectedHeap::string_dedup_cleaning(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
G1GCPhaseTimes* phase_times) {
G1StringDedupCleaningTask cl(is_alive, keep_alive, phase_times);
workers()->run_task(&cl);
}
// Weak Reference Processing support
bool G1STWIsAliveClosure::do_object_b(oop p) {
@ -3845,15 +3783,6 @@ void G1CollectedHeap::post_evacuate_collection_set(G1EvacuationInfo& evacuation_
WeakProcessor::weak_oops_do(workers(), &is_alive, &keep_alive, p->weak_phase_times());
if (G1StringDedup::is_enabled()) {
double string_dedup_time_ms = os::elapsedTime();
string_dedup_cleaning(&is_alive, &keep_alive, p);
double string_cleanup_time_ms = (os::elapsedTime() - string_dedup_time_ms) * 1000.0;
p->record_string_deduplication_time(string_cleanup_time_ms);
}
_allocator->release_gc_alloc_regions(evacuation_info);
post_evacuate_cleanup_1(per_thread_states, rdcqs);

View File

@ -1401,19 +1401,11 @@ public:
// after a full GC.
void rebuild_strong_code_roots();
// Partial cleaning of VM internal data structures.
void string_dedup_cleaning(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
G1GCPhaseTimes* phase_times = NULL);
// Performs cleaning of data structures after class unloading.
void complete_cleaning(BoolObjectClosure* is_alive, bool class_unloading_occurred);
// Verification
// Deduplicate the string
virtual void deduplicate_string(oop str);
// Perform any cleanup actions necessary before allowing a verification.
virtual void prepare_for_verify();

View File

@ -36,7 +36,6 @@
#include "gc/g1/g1OopClosures.inline.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/g1RegionMarkStatsCache.inline.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
#include "gc/g1/g1Trace.hpp"
#include "gc/g1/heapRegion.inline.hpp"
@ -1530,8 +1529,6 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
// Is alive closure.
G1CMIsAliveClosure g1_is_alive(_g1h);
// Inner scope to exclude the cleaning of the string table
// from the displayed time.
{
GCTraceTime(Debug, gc, phases) debug("Reference Processing", _gc_timer_cm);
@ -1630,9 +1627,6 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm);
_g1h->complete_cleaning(&g1_is_alive, purged_classes);
} else if (StringDedup::is_enabled()) {
GCTraceTime(Debug, gc, phases) debug("String Deduplication", _gc_timer_cm);
_g1h->string_dedup_cleaning(&g1_is_alive, NULL);
}
}

View File

@ -38,7 +38,6 @@
#include "gc/g1/g1OopClosures.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/g1RegionMarkStatsCache.inline.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/preservedMarks.hpp"
#include "gc/shared/referenceProcessor.hpp"
@ -271,10 +270,6 @@ void G1FullCollector::phase1_mark_live_objects() {
// Unload classes and purge the SystemDictionary.
bool purged_class = SystemDictionary::do_unloading(scope()->timer());
_heap->complete_cleaning(&_is_alive, purged_class);
} else if (G1StringDedup::is_enabled()) {
GCTraceTime(Debug, gc, phases) debug("Phase 1: String Dedup Cleanup", scope()->timer());
// If no class unloading just clean out string deduplication data.
_heap->string_dedup_cleaning(&_is_alive, NULL);
}
scope()->tracer()->report_object_count_after_gc(&_is_alive);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021, 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
@ -84,8 +84,7 @@ G1FullGCAdjustTask::G1FullGCAdjustTask(G1FullCollector* collector) :
_references_done(false),
_weak_proc_task(collector->workers()),
_hrclaimer(collector->workers()),
_adjust(collector),
_string_dedup_cleaning_task(NULL, &_adjust, false) {
_adjust(collector) {
// Need cleared claim bits for the roots processing
ClassLoaderDataGraph::clear_claimed_marks();
}
@ -110,9 +109,6 @@ void G1FullGCAdjustTask::work(uint worker_id) {
CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
_root_processor.process_all_roots(&_adjust, &adjust_cld, &adjust_code);
// Adjust string dedup data structures.
_string_dedup_cleaning_task.work(worker_id);
// Now adjust pointers region by region
G1AdjustRegionClosure blk(collector(), worker_id);
G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id);

View File

@ -28,9 +28,7 @@
#include "gc/g1/g1FullGCOopClosures.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/parallelCleaning.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "utilities/ticks.hpp"
@ -42,7 +40,6 @@ class G1FullGCAdjustTask : public G1FullGCTask {
WeakProcessor::Task _weak_proc_task;
HeapRegionClaimer _hrclaimer;
G1AdjustClosure _adjust;
StringDedupCleaningTask _string_dedup_cleaning_task;
public:
G1FullGCAdjustTask(G1FullCollector* collector);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021, 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
@ -28,7 +28,6 @@
#include "gc/g1/g1FullGCCompactionPoint.hpp"
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021, 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
@ -29,7 +29,6 @@
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "utilities/ticks.hpp"

View File

@ -28,6 +28,7 @@
#include "gc/g1/g1FullGCOopClosures.hpp"
#include "gc/g1/g1RegionMarkStatsCache.hpp"
#include "gc/shared/preservedMarks.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/taskqueue.hpp"
#include "memory/iterator.hpp"
#include "oops/markWord.hpp"
@ -59,10 +60,11 @@ class G1FullGCMarker : public CHeapObj<mtGC> {
PreservedMarks* _preserved_stack;
// Marking closures
G1MarkAndPushClosure _mark_closure;
G1VerifyOopClosure _verify_closure;
G1FollowStackClosure _stack_closure;
CLDToOopClosure _cld_closure;
G1MarkAndPushClosure _mark_closure;
G1VerifyOopClosure _verify_closure;
G1FollowStackClosure _stack_closure;
CLDToOopClosure _cld_closure;
StringDedup::Requests _string_dedup_requests;
G1RegionMarkStatsCache _mark_stats_cache;

View File

@ -34,8 +34,8 @@
#include "gc/g1/g1FullGCOopClosures.inline.hpp"
#include "gc/g1/g1RegionMarkStatsCache.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1StringDedupQueue.hpp"
#include "gc/shared/preservedMarks.inline.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "oops/access.inline.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/oop.inline.hpp"
@ -62,9 +62,10 @@ inline bool G1FullGCMarker::mark_object(oop obj) {
}
// Check if deduplicatable string.
if (G1StringDedup::is_enabled() &&
java_lang_String::is_instance_inlined(obj)) {
G1StringDedup::enqueue_from_mark(obj, _worker_id);
if (StringDedup::is_enabled() &&
java_lang_String::is_instance_inlined(obj) &&
G1StringDedup::is_candidate_from_mark(obj)) {
_string_dedup_requests.add(obj);
}
// Collect live words.

View File

@ -29,7 +29,6 @@
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"

View File

@ -29,7 +29,6 @@
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/taskqueue.hpp"

View File

@ -28,7 +28,6 @@
#include "gc/g1/g1GCPhaseTimes.hpp"
#include "gc/g1/g1HotCardCache.hpp"
#include "gc/g1/g1ParScanThreadState.inline.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageSet.hpp"
@ -139,14 +138,6 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) :
_gc_par_phases[OptTermination]->create_thread_work_items("Optional Termination Attempts:");
if (UseStringDeduplication) {
_gc_par_phases[StringDedupQueueFixup] = new WorkerDataArray<double>("StringDedupQueueFixup", "Queue Fixup (ms):", max_gc_threads);
_gc_par_phases[StringDedupTableFixup] = new WorkerDataArray<double>("StringDedupTableFixup", "Table Fixup (ms):", max_gc_threads);
} else {
_gc_par_phases[StringDedupQueueFixup] = NULL;
_gc_par_phases[StringDedupTableFixup] = NULL;
}
_gc_par_phases[RedirtyCards] = new WorkerDataArray<double>("RedirtyCards", "Redirty Logged Cards (ms):", max_gc_threads);
_gc_par_phases[RedirtyCards]->create_thread_work_items("Redirtied Cards:");
@ -166,7 +157,6 @@ void G1GCPhaseTimes::reset() {
_cur_optional_merge_heap_roots_time_ms = 0.0;
_cur_prepare_merge_heap_roots_time_ms = 0.0;
_cur_optional_prepare_merge_heap_roots_time_ms = 0.0;
_cur_string_deduplication_time_ms = 0.0;
_cur_prepare_tlab_time_ms = 0.0;
_cur_resize_tlab_time_ms = 0.0;
_cur_post_evacuate_cleanup_1_time_ms = 0.0;
@ -459,7 +449,6 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set() const {
_recorded_preserve_cm_referents_time_ms +
_cur_ref_proc_time_ms +
(_weak_phase_times.total_time_sec() * MILLIUNITS) +
_cur_string_deduplication_time_ms +
_cur_post_evacuate_cleanup_1_time_ms +
_cur_post_evacuate_cleanup_2_time_ms +
_recorded_total_rebuild_freelist_time_ms +
@ -475,12 +464,6 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set() const {
_weak_phase_times.log_total(2);
_weak_phase_times.log_subtotals(3);
if (G1StringDedup::is_enabled()) {
debug_time("String Deduplication", _cur_string_deduplication_time_ms);
debug_phase(_gc_par_phases[StringDedupQueueFixup], 1);
debug_phase(_gc_par_phases[StringDedupTableFixup], 1);
}
debug_time("Post Evacuate Cleanup 1", _cur_post_evacuate_cleanup_1_time_ms);
debug_phase(_gc_par_phases[MergePSS], 1);
debug_phase(_gc_par_phases[ClearCardTable], 1);

View File

@ -70,8 +70,6 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
Other,
GCWorkerTotal,
GCWorkerEnd,
StringDedupQueueFixup,
StringDedupTableFixup,
RedirtyCards,
FreeCollectionSet,
YoungFreeCSet,
@ -146,8 +144,6 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
double _cur_optional_evac_time_ms;
double _cur_collection_code_root_fixup_time_ms;
double _cur_string_deduplication_time_ms;
double _cur_merge_heap_roots_time_ms;
double _cur_optional_merge_heap_roots_time_ms;
@ -293,10 +289,6 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
_cur_optional_prepare_merge_heap_roots_time_ms += ms;
}
void record_string_deduplication_time(double ms) {
_cur_string_deduplication_time_ms = ms;
}
void record_ref_proc_time(double ms) {
_cur_ref_proc_time_ms = ms;
}

View File

@ -33,7 +33,6 @@
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
@ -518,11 +517,6 @@ void G1HeapVerifier::verify(VerifyOption vo) {
}
}
if (G1StringDedup::is_enabled()) {
log_debug(gc, verify)("StrDedup");
G1StringDedup::verify();
}
if (failures) {
log_error(gc, verify)("Heap after failed verification (kind %d):", vo);
// It helps to have the per-region information in the output to

View File

@ -23,7 +23,6 @@
*/
#include "precompiled.hpp"
#include "classfile/vmClasses.hpp"
#include "gc/g1/g1Allocator.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1CollectionSet.hpp"
@ -33,6 +32,7 @@
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1Trace.hpp"
#include "gc/shared/partialArrayTaskStepper.inline.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/taskqueue.inline.hpp"
#include "memory/allocation.inline.hpp"
#include "oops/access.inline.hpp"
@ -76,17 +76,11 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h,
_old_gen_is_full(false),
_partial_objarray_chunk_size(ParGCArrayScanChunk),
_partial_array_stepper(n_workers),
_string_klass_or_null(G1StringDedup::is_enabled()
? vmClasses::String_klass()
: nullptr),
_string_dedup_requests(),
_num_optional_regions(optional_cset_length),
_numa(g1h->numa()),
_obj_alloc_stat(NULL)
{
// Verify klass comparison with _string_klass_or_null is sufficient
// to determine whether dedup is enabled and the object is a String.
assert(vmClasses::String_klass()->is_final(), "precondition");
// We allocate number of young gen regions in the collection set plus one
// entries, since entry 0 keeps track of surviving bytes for non-young regions.
// We also add a few elements at the beginning and at the end in
@ -517,20 +511,14 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio
return obj;
}
// StringDedup::is_enabled() and java_lang_String::is_instance_inline
// test of the obj, combined into a single comparison, using the klass
// already in hand and avoiding the null check in is_instance.
if (klass == _string_klass_or_null) {
const bool is_from_young = region_attr.is_young();
const bool is_to_young = dest_attr.is_young();
assert(is_from_young == from_region->is_young(),
"sanity");
assert(is_to_young == _g1h->heap_region_containing(obj)->is_young(),
"sanity");
G1StringDedup::enqueue_from_evacuation(is_from_young,
is_to_young,
_worker_id,
obj);
// Check for deduplicating young Strings.
if (G1StringDedup::is_candidate_from_evacuation(klass,
region_attr,
dest_attr,
age)) {
// Record old; request adds a new weak reference, which reference
// processing expects to refer to a from-space object.
_string_dedup_requests.add(old);
}
G1ScanInYoungSetter x(&_scanner, dest_attr.is_young());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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,6 +33,7 @@
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/shared/ageTable.hpp"
#include "gc/shared/partialArrayTaskStepper.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/taskqueue.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
@ -42,7 +43,6 @@ class G1OopStarChunkedList;
class G1PLABAllocator;
class G1EvacuationRootClosures;
class HeapRegion;
class Klass;
class outputStream;
class G1ParScanThreadState : public CHeapObj<mtGC> {
@ -84,8 +84,7 @@ class G1ParScanThreadState : public CHeapObj<mtGC> {
// Size (in elements) of a partial objArray task chunk.
int _partial_objarray_chunk_size;
PartialArrayTaskStepper _partial_array_stepper;
// Used to check whether string dedup should be applied to an object.
Klass* _string_klass_or_null;
StringDedup::Requests _string_dedup_requests;
G1CardTable* ct() { return _ct; }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2021, 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
@ -53,11 +53,9 @@ void JVMCICleaningTask::work(bool unloading_occurred) {
G1ParallelCleaningTask::G1ParallelCleaningTask(BoolObjectClosure* is_alive,
uint num_workers,
bool unloading_occurred,
bool resize_dedup_table) :
bool unloading_occurred) :
AbstractGangTask("G1 Parallel Cleaning"),
_unloading_occurred(unloading_occurred),
_string_dedup_task(is_alive, NULL, resize_dedup_table),
_code_cache_task(num_workers, is_alive, unloading_occurred),
JVMCI_ONLY(_jvmci_cleaning_task() COMMA)
_klass_cleaning_task() {
@ -72,9 +70,6 @@ void G1ParallelCleaningTask::work(uint worker_id) {
// Do first pass of code cache cleaning.
_code_cache_task.work(worker_id);
// Clean the string dedup data structures.
_string_dedup_task.work(worker_id);
// Clean all klasses that were not unloaded.
// The weak metadata in klass doesn't need to be
// processed if there was no unloading.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2021, 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
@ -46,7 +46,6 @@ private:
class G1ParallelCleaningTask : public AbstractGangTask {
private:
bool _unloading_occurred;
StringDedupCleaningTask _string_dedup_task;
CodeCacheUnloadingTask _code_cache_task;
#if INCLUDE_JVMCI
JVMCICleaningTask _jvmci_cleaning_task;
@ -57,8 +56,7 @@ public:
// The constructor is run in the VMThread.
G1ParallelCleaningTask(BoolObjectClosure* is_alive,
uint num_workers,
bool unloading_occurred,
bool resize_dedup_table);
bool unloading_occurred);
void work(uint worker_id);
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -23,62 +23,15 @@
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1GCPhaseTimes.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1StringDedupQueue.hpp"
#include "gc/g1/g1StringDedupStat.hpp"
#include "gc/shared/stringdedup/stringDedup.inline.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "gc/shared/stringdedup/stringDedupTable.hpp"
#include "gc/shared/stringdedup/stringDedupThread.inline.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "oops/oop.inline.hpp"
void G1StringDedup::initialize() {
assert(UseG1GC, "String deduplication available with G1");
StringDedup::initialize_impl<G1StringDedupQueue, G1StringDedupStat>();
}
bool G1StringDedup::is_candidate_from_mark(oop obj) {
bool from_young = G1CollectedHeap::heap()->heap_region_containing(obj)->is_young();
bool G1StringDedup::is_candidate_from_mark(oop java_string) {
// Candidate if string is being evacuated from young to old but has not
// reached the deduplication age threshold, i.e. has not previously been a
// candidate during its life in the young generation.
return from_young && (obj->age() < StringDeduplicationAgeThreshold);
}
void G1StringDedup::enqueue_from_mark(oop java_string, uint worker_id) {
assert(is_enabled(), "String deduplication not enabled");
assert(java_lang_String::is_instance(java_string), "not a String");
if (is_candidate_from_mark(java_string)) {
G1StringDedupQueue::push(worker_id, java_string);
}
}
bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) {
if (from_young) {
if (to_young && obj->age() == StringDeduplicationAgeThreshold) {
// Candidate found. String is being evacuated from young to young and just
// reached the deduplication age threshold.
return true;
}
if (!to_young && obj->age() < StringDeduplicationAgeThreshold) {
// Candidate found. String is being evacuated from young to old but has not
// reached the deduplication age threshold, i.e. has not previously been a
// candidate during its life in the young generation.
return true;
}
}
// Not a candidate
return false;
}
void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint worker_id, oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
assert(java_lang_String::is_instance(java_string), "not a String");
if (is_candidate_from_evacuation(from_young, to_young, java_string)) {
G1StringDedupQueue::push(worker_id, java_string);
}
return G1CollectedHeap::heap()->heap_region_containing(java_string)->is_young() &&
StringDedup::is_below_threshold_age(java_string->age());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -48,37 +48,32 @@
// This approach avoids making the same object a candidate more than once.
//
#include "gc/g1/g1HeapRegionAttr.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
class OopClosure;
class BoolObjectClosure;
class G1GCPhaseTimes;
class G1StringDedupUnlinkOrOopsDoClosure;
//
// G1 interface for interacting with string deduplication.
//
class G1StringDedup : public StringDedup {
private:
// Candidate selection policies, returns true if the given object is
// candidate for string deduplication.
static bool is_candidate_from_mark(oop java_string);
static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop java_string);
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
class G1StringDedup : AllStatic {
public:
// Initialize string deduplication.
static void initialize();
// Candidate selection policy for full GC, returning true if the given
// String is a candidate for string deduplication.
// precondition: StringDedup::is_enabled()
// precondition: java_string is a Java String
static bool is_candidate_from_mark(oop java_string);
// Enqueues a deduplication candidate for later processing by the deduplication
// thread. Before enqueuing, these functions apply the appropriate candidate
// selection policy to filters out non-candidates.
// Precondition for both is that java_string is a String.
static void enqueue_from_mark(oop java_string, uint worker_id);
static void enqueue_from_evacuation(bool from_young, bool to_young,
unsigned int queue, oop java_string);
// Candidate selection policy for young/mixed GC.
// If to is young then age should be the new (survivor's) age.
// if to is old then age should be the age of the copied from object.
static bool is_candidate_from_evacuation(const Klass* klass,
G1HeapRegionAttr from,
G1HeapRegionAttr to,
uint age) {
return StringDedup::is_enabled_string(klass) &&
from.is_young() &&
(to.is_young() ?
StringDedup::is_threshold_age(age) :
StringDedup::is_below_threshold_age(age));
}
};
#endif // SHARE_GC_G1_G1STRINGDEDUP_HPP

View File

@ -1,151 +0,0 @@
/*
* Copyright (c) 2014, 2019, 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 "classfile/javaClasses.inline.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1StringDedupQueue.hpp"
#include "logging/log.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "utilities/stack.inline.hpp"
const size_t G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue
const size_t G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue
G1StringDedupQueue::G1StringDedupQueue() :
_cursor(0),
_cancel(false),
_empty(true),
_dropped(0) {
_nqueues = ParallelGCThreads;
_queues = NEW_C_HEAP_ARRAY(G1StringDedupWorkerQueue, _nqueues, mtGC);
for (size_t i = 0; i < _nqueues; i++) {
new (_queues + i) G1StringDedupWorkerQueue(G1StringDedupWorkerQueue::default_segment_size(), _max_cache_size, _max_size);
}
}
G1StringDedupQueue::~G1StringDedupQueue() {
ShouldNotReachHere();
}
void G1StringDedupQueue::wait_impl() {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
while (_empty && !_cancel) {
ml.wait();
}
}
void G1StringDedupQueue::cancel_wait_impl() {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
_cancel = true;
ml.notify();
}
void G1StringDedupQueue::push_impl(uint worker_id, oop java_string) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
assert(worker_id < _nqueues, "Invalid queue");
// Push and notify waiter
G1StringDedupWorkerQueue& worker_queue = _queues[worker_id];
if (!worker_queue.is_full()) {
worker_queue.push(java_string);
if (_empty) {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
if (_empty) {
// Mark non-empty and notify waiter
_empty = false;
ml.notify();
}
}
} else {
// Queue is full, drop the string and update the statistics
Atomic::inc(&_dropped);
}
}
oop G1StringDedupQueue::pop_impl() {
assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint");
NoSafepointVerifier nsv;
// Try all queues before giving up
for (size_t tries = 0; tries < _nqueues; tries++) {
// The cursor indicates where we left of last time
G1StringDedupWorkerQueue* queue = &_queues[_cursor];
while (!queue->is_empty()) {
oop obj = queue->pop();
// The oop we pop can be NULL if it was marked
// dead. Just ignore those and pop the next oop.
if (obj != NULL) {
return obj;
}
}
// Try next queue
_cursor = (_cursor + 1) % _nqueues;
}
// Mark empty
_empty = true;
return NULL;
}
void G1StringDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
assert(queue < _nqueues, "Invalid queue");
StackIterator<oop, mtGC> iter(_queues[queue]);
while (!iter.is_empty()) {
oop* p = iter.next_addr();
if (*p != NULL) {
if (cl->is_alive(*p)) {
cl->keep_alive(p);
} else {
// Clear dead reference
*p = NULL;
}
}
}
}
void G1StringDedupQueue::print_statistics_impl() {
log_debug(gc, stringdedup)(" Queue");
log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _dropped);
}
void G1StringDedupQueue::verify_impl() {
for (size_t i = 0; i < _nqueues; i++) {
StackIterator<oop, mtGC> iter(_queues[i]);
while (!iter.is_empty()) {
oop obj = iter.next();
if (obj != NULL) {
guarantee(G1CollectedHeap::heap()->is_in_reserved(obj), "Object must be on the heap");
guarantee(!obj->is_forwarded(), "Object must not be forwarded");
guarantee(java_lang_String::is_instance(obj), "Object must be a String");
}
}
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) 2014, 2019, 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_G1_G1STRINGDEDUPQUEUE_HPP
#define SHARE_GC_G1_G1STRINGDEDUPQUEUE_HPP
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
#include "utilities/stack.hpp"
class StringDedupUnlinkOrOopsDoClosure;
//
// G1 enqueues candidates during the stop-the-world mark/evacuation phase.
//
class G1StringDedupQueue : public StringDedupQueue {
private:
typedef Stack<oop, mtGC> G1StringDedupWorkerQueue;
static const size_t _max_size;
static const size_t _max_cache_size;
G1StringDedupWorkerQueue* _queues;
size_t _nqueues;
size_t _cursor;
bool _cancel;
volatile bool _empty;
// Statistics counter, only used for logging.
uintx _dropped;
~G1StringDedupQueue();
void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
public:
G1StringDedupQueue();
protected:
// Blocks and waits for the queue to become non-empty.
void wait_impl();
// Wakes up any thread blocked waiting for the queue to become non-empty.
void cancel_wait_impl();
// Pushes a deduplication candidate onto a specific GC worker queue.
void push_impl(uint worker_id, oop java_string);
// Pops a deduplication candidate from any queue, returns NULL if
// all queues are empty.
oop pop_impl();
size_t num_queues() const {
return _nqueues;
}
void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
void print_statistics_impl();
void verify_impl();
};
#endif // SHARE_GC_G1_G1STRINGDEDUPQUEUE_HPP

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2018, 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/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1StringDedupStat.hpp"
#include "logging/log.hpp"
G1StringDedupStat::G1StringDedupStat() : StringDedupStat(),
_deduped_young(0),
_deduped_young_bytes(0),
_deduped_old(0),
_deduped_old_bytes(0),
_heap(G1CollectedHeap::heap()) {
}
void G1StringDedupStat::deduped(oop obj, uintx bytes) {
StringDedupStat::deduped(obj, bytes);
if (_heap->is_in_young(obj)) {
_deduped_young ++;
_deduped_young_bytes += bytes;
} else {
_deduped_old ++;
_deduped_old_bytes += bytes;
}
}
void G1StringDedupStat::add(const StringDedupStat* const stat) {
StringDedupStat::add(stat);
const G1StringDedupStat* const g1_stat = (const G1StringDedupStat* const)stat;
_deduped_young += g1_stat->_deduped_young;
_deduped_young_bytes += g1_stat->_deduped_young_bytes;
_deduped_old += g1_stat->_deduped_old;
_deduped_old_bytes += g1_stat->_deduped_old_bytes;
}
void G1StringDedupStat::print_statistics(bool total) const {
StringDedupStat::print_statistics(total);
double deduped_young_percent = percent_of(_deduped_young, _deduped);
double deduped_young_bytes_percent = percent_of(_deduped_young_bytes, _deduped_bytes);
double deduped_old_percent = percent_of(_deduped_old, _deduped);
double deduped_old_bytes_percent = percent_of(_deduped_old_bytes, _deduped_bytes);
log_debug(gc, stringdedup)(" Young: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")",
_deduped_young, deduped_young_percent, STRDEDUP_BYTES_PARAM(_deduped_young_bytes), deduped_young_bytes_percent);
log_debug(gc, stringdedup)(" Old: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")",
_deduped_old, deduped_old_percent, STRDEDUP_BYTES_PARAM(_deduped_old_bytes), deduped_old_bytes_percent);
}
void G1StringDedupStat::reset() {
StringDedupStat::reset();
_deduped_young = 0;
_deduped_young_bytes = 0;
_deduped_old = 0;
_deduped_old_bytes = 0;
}

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) 2018, 2019, 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_G1_G1STRINGDEDUPSTAT_HPP
#define SHARE_GC_G1_G1STRINGDEDUPSTAT_HPP
#include "gc/shared/stringdedup/stringDedupStat.hpp"
// G1 extension for gathering/reporting generational statistics
class G1StringDedupStat : public StringDedupStat {
private:
uintx _deduped_young;
uintx _deduped_young_bytes;
uintx _deduped_old;
uintx _deduped_old_bytes;
G1CollectedHeap* const _heap;
public:
G1StringDedupStat();
void deduped(oop obj, uintx bytes);
void add(const StringDedupStat* const stat);
void print_statistics(bool total) const;
void reset();
};
#endif // SHARE_GC_G1_G1STRINGDEDUPSTAT_HPP

View File

@ -31,6 +31,7 @@
#include "gc/shared/collectedHeap.inline.hpp"
#include "gc/shared/gcLocker.inline.hpp"
#include "gc/shared/gcHeapSummary.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/gcTrace.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/gcVMOperations.hpp"
@ -582,6 +583,7 @@ void CollectedHeap::initialize_reserved_region(const ReservedHeapSpace& rs) {
}
void CollectedHeap::post_initialize() {
StringDedup::initialize();
initialize_serviceability();
}
@ -637,10 +639,6 @@ bool CollectedHeap::is_archived_object(oop object) const {
return false;
}
void CollectedHeap::deduplicate_string(oop str) {
// Do nothing, unless overridden in subclass.
}
uint32_t CollectedHeap::hash_oop(oop obj) const {
const uintptr_t addr = cast_from_oop<uintptr_t>(obj);
return static_cast<uint32_t>(addr >> LogMinObjAlignment);

View File

@ -481,9 +481,6 @@ class CollectedHeap : public CHeapObj<mtInternal> {
// Is the given object inside a CDS archive area?
virtual bool is_archived_object(oop object) const;
// Deduplicate the string, iff the GC supports string deduplication.
virtual void deduplicate_string(oop str);
virtual bool is_oop(oop object) const;
// Non product verification and debugging.
#ifndef PRODUCT

View File

@ -537,7 +537,7 @@
"in a comma separated string. Sub-systems are: " \
"threads, heap, symbol_table, string_table, codecache, " \
"dictionary, classloader_data_graph, metaspace, jni_handles, " \
"codecache_oops") \
"codecache_oops, resolved_method_table, stringdedup") \
\
product(bool, GCParallelVerificationEnabled, true, DIAGNOSTIC, \
"Enable parallel memory system verification") \

View File

@ -38,7 +38,7 @@ class OopStorageSet : public AllStatic {
// Must be updated when new OopStorages are introduced
static const uint strong_count = 4 JVMTI_ONLY(+ 1);
static const uint weak_count = 5 JVMTI_ONLY(+ 1) JFR_ONLY(+ 1);
static const uint weak_count = 8 JVMTI_ONLY(+ 1) JFR_ONLY(+ 1);
static const uint all_count = strong_count + weak_count;
static const uint all_start = 0;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, 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
@ -32,29 +32,6 @@
#include "logging/log.hpp"
#include "runtime/atomic.hpp"
StringDedupCleaningTask::StringDedupCleaningTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool resize_table) :
AbstractGangTask("String Dedup Cleaning"),
_dedup_closure(is_alive, keep_alive) {
if (StringDedup::is_enabled()) {
StringDedup::gc_prologue(resize_table);
}
}
StringDedupCleaningTask::~StringDedupCleaningTask() {
if (StringDedup::is_enabled()) {
StringDedup::gc_epilogue();
}
}
void StringDedupCleaningTask::work(uint worker_id) {
if (StringDedup::is_enabled()) {
StringDedup::parallel_unlink(&_dedup_closure, worker_id);
}
}
CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred) :
_unloading_scope(is_alive),
_unloading_occurred(unloading_occurred),

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, 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
@ -28,19 +28,8 @@
#include "classfile/classLoaderDataGraph.hpp"
#include "code/codeCache.hpp"
#include "gc/shared/oopStorageParState.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/workgroup.hpp"
class StringDedupCleaningTask : public AbstractGangTask {
StringDedupUnlinkOrOopsDoClosure _dedup_closure;
public:
StringDedupCleaningTask(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool resize_table);
~StringDedupCleaningTask();
void work(uint worker_id);
};
class CodeCacheUnloadingTask {
CodeCache::UnloadingScope _unloading_scope;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,61 +21,182 @@
* questions.
*
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "gc/shared/gcLogPrecious.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "gc/shared/stringdedup/stringDedupConfig.hpp"
#include "gc/shared/stringdedup/stringDedupProcessor.hpp"
#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"
#include "oops/access.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/markWord.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/globals.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
bool StringDedup::_initialized = false;
bool StringDedup::_enabled = false;
void StringDedup::gc_prologue(bool resize_and_rehash_table) {
assert(is_enabled(), "String deduplication not enabled");
StringDedupQueue::gc_prologue();
StringDedupTable::gc_prologue(resize_and_rehash_table);
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;
bool StringDedup::ergo_initialize() {
return Config::ergo_initialize();
}
void StringDedup::gc_epilogue() {
assert(is_enabled(), "String deduplication not enabled");
StringDedupQueue::gc_epilogue();
StringDedupTable::gc_epilogue();
void StringDedup::initialize() {
assert(!_initialized, "already initialized");
// Unconditionally create the oopstorage objects, to simplify usage
// elsewhere. OopStorageSet and clients don't support optional oopstorage
// objects.
Table::initialize_storage();
Processor::initialize_storage();
if (UseStringDeduplication) {
Config::initialize();
// Verify klass comparison with _string_klass_or_null is sufficient
// to determine whether dedup is enabled and the object is a String.
assert(vmClasses::String_klass()->is_final(), "precondition");
_string_klass_or_null = vmClasses::String_klass();
_enabled_age_threshold = Config::age_threshold();
_enabled_age_limit = Config::age_threshold();
Table::initialize();
Processor::initialize();
_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() {
assert(is_enabled(), "String deduplication not enabled");
StringDedupThread::thread()->stop();
}
void StringDedup::deduplicate(oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
StringDedupStat dummy; // Statistics from this path is never used
StringDedupTable::deduplicate(java_string, &dummy);
}
void StringDedup::parallel_unlink(StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) {
assert(is_enabled(), "String deduplication not enabled");
StringDedupQueue::unlink_or_oops_do(unlink);
StringDedupTable::unlink_or_oops_do(unlink, worker_id);
assert(is_enabled(), "precondition");
assert(_processor != nullptr, "invariant");
_processor->stop();
}
void StringDedup::threads_do(ThreadClosure* tc) {
assert(is_enabled(), "String deduplication not enabled");
tc->do_thread(StringDedupThread::thread());
assert(is_enabled(), "precondition");
assert(_processor != nullptr, "invariant");
tc->do_thread(_processor);
}
void StringDedup::notify_intern(oop java_string) {
assert(is_enabled(), "precondition");
// A String that is interned in the StringTable must not later have its
// underlying byte array changed, so mark it as not deduplicatable. But we
// can still add the byte array to the dedup table for sharing, so add the
// string to the pending requests. Triggering request processing is left
// to the next GC.
{
MutexLocker ml(StringDedupIntern_lock, Mutex::_no_safepoint_check_flag);
java_lang_String::set_deduplication_forbidden(java_string);
}
StorageUse* requests = Processor::storage_for_requests();
oop* ref = requests->storage()->allocate();
if (ref != nullptr) {
NativeAccess<ON_PHANTOM_OOP_REF>::oop_store(ref, java_string);
log_trace(stringdedup)("StringDedup::deduplicate");
}
requests->relinquish();
}
StringDedup::Requests::Requests() :
_storage_for_requests(nullptr), _buffer(nullptr), _index(0), _refill_failed(false)
{}
StringDedup::Requests::~Requests() {
flush();
}
bool StringDedup::Requests::refill_buffer() {
assert(_index == 0, "precondition");
// Treat out of memory failure as sticky; don't keep retrying.
if (_refill_failed) return false;
// Lazy initialization of the requests object. It can be common for
// many of the marking threads to not encounter any candidates.
const size_t buffer_size = OopStorage::bulk_allocate_limit;
if (_buffer == nullptr) {
// Lazily allocate a buffer to hold pre-allocated storage entries.
_buffer = NEW_C_HEAP_ARRAY_RETURN_NULL(oop*, buffer_size, mtStringDedup);
if (_buffer == nullptr) {
log_debug(stringdedup)("request failed to allocate buffer");
_refill_failed = true;
return false;
}
// Lazily obtain the storage object to use for requests.
assert(_storage_for_requests == nullptr, "invariant");
_storage_for_requests = Processor::storage_for_requests();
}
assert(_storage_for_requests != nullptr, "invariant");
// Bulk pre-allocate some storage entries to satisfy this and future
// requests. This amortizes the cost of allocating entries over
// multiple requests, and reduces contention on the storage object.
_index = _storage_for_requests->storage()->allocate(_buffer, buffer_size);
if (_index == 0) {
log_debug(stringdedup)("request failed to allocate oopstorage entries");
flush();
_refill_failed = true;
return false;
}
return true;
}
void StringDedup::Requests::add(oop java_string) {
assert(is_enabled(), "StringDedup not enabled");
if ((_index == 0) && !refill_buffer()) return;
// Store the string in the next pre-allocated storage entry.
oop* ref = _buffer[--_index];
NativeAccess<ON_PHANTOM_OOP_REF>::oop_store(ref, java_string);
log_trace(stringdedup)("request");
}
void StringDedup::Requests::flush() {
if (_buffer != nullptr) {
if (_index > 0) {
assert(_storage_for_requests != nullptr, "invariant");
_storage_for_requests->storage()->release(_buffer, _index);
}
FREE_C_HEAP_ARRAY(oop*, _buffer);
_buffer = nullptr;
}
if (_storage_for_requests != nullptr) {
_storage_for_requests->relinquish();
_storage_for_requests = nullptr;
}
_index = 0;
_refill_failed = false;
}
void StringDedup::verify() {
assert(is_enabled(), "String deduplication not enabled");
StringDedupQueue::verify();
StringDedupTable::verify();
}
StringDedupUnlinkOrOopsDoClosure::StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
OopClosure* keep_alive) :
_always_true(),
_do_nothing(),
_is_alive(is_alive != NULL ? is_alive : &_always_true),
_keep_alive(keep_alive != NULL ? keep_alive : &_do_nothing) {
assert_at_safepoint();
if (is_enabled()) {
Table::verify();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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,108 +25,186 @@
#ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP
//
// String Deduplication
//
// String deduplication aims to reduce the heap live-set by deduplicating identical
// instances of String so that they share the same backing character array.
// String deduplication aims to reduce the heap live-set by modifying equal
// instances of java.lang.String so they share the same backing byte array
// (the String's value).
//
// The deduplication process is divided in two main parts, 1) finding the objects to
// deduplicate, and 2) deduplicating those objects. The first part is done as part of
// a normal GC cycle when objects are marked or evacuated. At this time a check is
// applied on each object to check if it is a candidate for deduplication. If so, the
// object is placed on the deduplication queue for later processing. The second part,
// processing the objects on the deduplication queue, is a concurrent phase which
// starts right after the stop-the-wold marking/evacuation phase. This phase is
// executed by the deduplication thread, which pulls deduplication candidates of the
// deduplication queue and tries to deduplicate them.
// The deduplication process is divided in two main parts, 1) finding the
// objects to deduplicate, and 2) deduplicating those objects.
//
// A deduplication hashtable is used to keep track of all unique character arrays
// used by String objects. When deduplicating, a lookup is made in this table to see
// if there is already an identical character array somewhere on the heap. If so, the
// String object is adjusted to point to that character array, releasing the reference
// to the original array allowing it to eventually be garbage collected. If the lookup
// fails the character array is instead inserted into the hashtable so that this array
// can be shared at some point in the future.
// The first part is done as part of a normal GC cycle when objects are
// marked or evacuated. At this time a check is applied on each object to
// determine whether it is a candidate for deduplication. Candidates are
// added to the set of deduplication requests for later processing.
//
// Candidate selection criteria is GC specific.
// The second part, processing the deduplication requests, is a concurrent
// phase. This phase is executed by the deduplication thread, which takes
// candidates from the set of requests and tries to deduplicate them.
//
// Interned strings are a bit special. They are explicitly deduplicated just before
// being inserted into the StringTable (to avoid counteracting C2 optimizations done
// on string literals), then they also become deduplication candidates if they reach
// the deduplication age threshold or are evacuated to an old heap region. The second
// attempt to deduplicate such strings will be in vain, but we have no fast way of
// filtering them out. This has not shown to be a problem, as the number of interned
// strings is usually dwarfed by the number of normal (non-interned) strings.
// A deduplication table is used to keep track of unique byte arrays used by
// String objects. When deduplicating, a lookup is made in this table to
// see if there is already an equivalent byte array that was used by some
// other String. If so, the String object is adjusted to point to that byte
// array, and the original array is released, allowing it to eventually be
// garbage collected. If the lookup fails the byte array is instead
// inserted into the table so it can potentially be shared with other
// Strings in the future.
//
// The set of requests uses entries from a pair of weak OopStorage objects.
// One is used for requests, the other is being processed. When processing
// completes, the roles of the storage objects are exchanged. The GC adds
// entries referring to discovered candidates, allocating new OopStorage
// entries for the requests. The deduplication processing thread does a
// concurrent iteration over the processing storage, deduplicating the
// Strings and releasing the OopStorage entries. Two storage objects are
// used so there isn't any conflict between adding and removing entries by
// different threads.
//
// The deduplication table uses entries from another weak OopStorage to hold
// the byte arrays. This permits reclamation of arrays that become unused.
// This is separate from the request storage objects because dead count
// tracking is used by the table implementation as part of resizing
// decisions and for deciding when to cleanup dead entries in the table.
// The usage pattern for the table is also very different from that of the
// request storages. The request/processing storages are used in a way that
// supports bulk allocation and release of entries.
//
// Candidate selection criteria is GC specific. This class provides some
// helper functions that may be of use when implementing candidate
// selection.
//
// Strings interned in the StringTable require special handling. Once a
// String has been added to the StringTable, its byte array must not change.
// Doing so would counteract C2 optimizations on string literals. But an
// interned string might later become a deduplication candidate through the
// normal GC discovery mechanism. To prevent such modifications, the
// deduplication_forbidden flag of a String is set before interning it. A
// String with that flag set may have its byte array added to the
// deduplication table, but will not have its byte array replaced by a
// different but equivalent array from the table.
//
// A GC must opt-in to support string deduplication. This primarily involves
// making deduplication requests. As the GC is processing objects it must
// determine which are candidates for deduplication, and add those objects
// to StringDedup::Requests objects. Typically, each GC marking/evacuation
// thread has its own Requests object. Once liveness analysis is complete,
// but before weak reference processing, the GC should flush or delete all
// of its Requests objects.
//
// For additional information on string deduplication, please see JEP 192,
// http://openjdk.java.net/jeps/192
//
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "gc/shared/stringdedup/stringDedupStat.hpp"
#include "gc/shared/stringdedup/stringDedupTable.hpp"
#include "memory/allocation.hpp"
#include "runtime/thread.hpp"
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
#include "utilities/globalDefinitions.hpp"
class Klass;
class ThreadClosure;
//
// Main interface for interacting with string deduplication.
//
// The StringDedup class provides the API for the deduplication mechanism.
// StringDedup::Requests and the StringDedup functions for candidate testing
// are all that a GC needs to use to support the string deduplication
// feature. Other functions in the StringDedup class are called where
// needed, without requiring GC-specific code.
class StringDedup : public AllStatic {
private:
// Single state for checking if string deduplication is enabled.
class Config;
class Processor;
class Stat;
class StorageUse;
class Table;
static bool _initialized;
static bool _enabled;
public:
// Returns true if string deduplication is enabled.
static bool is_enabled() {
return _enabled;
}
static Processor* _processor;
static Stat _cur_stat;
static Stat _total_stat;
// Stop the deduplication thread.
static const Klass* _string_klass_or_null;
static uint _enabled_age_threshold;
static uint _enabled_age_limit;
public:
class Requests;
// Initialize and check command line arguments.
// Returns true if configuration is valid, false otherwise.
static bool ergo_initialize();
// Initialize deduplication if enabled by command line arguments.
static void initialize();
// Returns true if string deduplication is enabled.
static bool is_enabled() { return _enabled; }
// Stop the deduplication processor thread.
// precondition: is_enabled()
static void stop();
// Immediately deduplicates the given String object, bypassing the
// the deduplication queue.
static void deduplicate(oop java_string);
static void parallel_unlink(StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id);
// Visit the deduplication processor thread.
// precondition: is_enabled()
static void threads_do(ThreadClosure* tc);
// Notify that a String is being added to the StringTable.
// precondition: is_enabled()
// precondition: java_string is a Java String object.
static void notify_intern(oop java_string);
// precondition: at safepoint
static void verify();
// GC support
static void gc_prologue(bool resize_and_rehash_table);
static void gc_epilogue();
// Some predicates for use in testing whether an object is a candidate for
// deduplication. These functions combine an implicit is_enabled check
// with another check in a single comparison.
protected:
// Initialize string deduplication.
// Q: String Dedup Queue implementation
// S: String Dedup Stat implementation
template <typename Q, typename S>
static void initialize_impl();
// Return true if k is String klass and deduplication is enabled.
static bool is_enabled_string(const Klass* k) {
return k == _string_klass_or_null;
}
// Return true if age == StringDeduplicationAgeThreshold and
// deduplication is enabled.
static bool is_threshold_age(uint age) {
// Threshold is from option if enabled, or an impossible value (exceeds
// markWord::max_age) if disabled.
return age == _enabled_age_threshold;
}
// Return true if age < StringDeduplicationAgeThreshold and
// deduplication is enabled.
static bool is_below_threshold_age(uint age) {
// Limit is from option if enabled, or 0 if disabled.
return age < _enabled_age_limit;
}
};
// GC requests for String deduplication.
//
// This closure encapsulates the closures needed when scanning
// the deduplication queue and table during the unlink_or_oops_do() operation.
//
class StringDedupUnlinkOrOopsDoClosure : public StackObj {
AlwaysTrueClosure _always_true;
DoNothingClosure _do_nothing;
BoolObjectClosure* _is_alive;
OopClosure* _keep_alive;
// Each marking thread should have it's own Requests object. When marking
// is completed the Requests object must be flushed (either explicitly or by
// the destructor).
class StringDedup::Requests {
StorageUse* _storage_for_requests;
oop** _buffer;
size_t _index;
bool _refill_failed;
bool refill_buffer();
public:
StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
OopClosure* keep_alive);
Requests();
~Requests(); // Calls flush().
bool is_alive(oop o) { return _is_alive->do_object_b(o); }
// Request deduplication of java_string.
// prerequisite: StringDedup::is_enabled()
// prerequisite: java_string is a Java String
void add(oop java_string);
void keep_alive(oop* p) { _keep_alive->do_oop(p); }
// Flush any buffered deduplication requests and release resources
// used by this object.
void flush();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP

View File

@ -1,41 +0,0 @@
/*
* Copyright (c) 2018, 2019, 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_STRINGDEDUP_INLINE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupThread.inline.hpp"
template <typename Q, typename S>
void StringDedup::initialize_impl() {
if (UseStringDeduplication) {
_enabled = true;
StringDedupQueue::create<Q>();
StringDedupTable::create();
StringDedupThreadImpl<S>::create();
}
}
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2021, 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 "classfile/altHashing.hpp"
#include "gc/shared/stringdedup/stringDedupConfig.hpp"
#include "logging/log.hpp"
#include "runtime/flags/jvmFlag.hpp"
#include "runtime/globals.hpp"
#include "runtime/globals_extension.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
size_t StringDedup::Config::_initial_table_size;
int StringDedup::Config::_age_threshold;
double StringDedup::Config::_load_factor_for_growth;
double StringDedup::Config::_load_factor_for_shrink;
double StringDedup::Config::_load_factor_target;
size_t StringDedup::Config::_minimum_dead_for_cleanup;
double StringDedup::Config::_dead_factor_for_cleanup;
uint64_t StringDedup::Config::_hash_seed;
size_t StringDedup::Config::initial_table_size() {
return _initial_table_size;
}
int StringDedup::Config::age_threshold() {
return _age_threshold;
}
bool StringDedup::Config::should_cleanup_table(size_t entry_count, size_t dead_count) {
return (dead_count > _minimum_dead_for_cleanup) &&
(dead_count > (entry_count * _dead_factor_for_cleanup));
}
uint64_t StringDedup::Config::hash_seed() {
return _hash_seed;
}
static uint64_t initial_hash_seed() {
if (StringDeduplicationHashSeed != 0) {
return StringDeduplicationHashSeed;
} else {
return AltHashing::compute_seed();
}
}
// Primes after 500 * 2^N and 500 * (2^N + 2^(N-1)) for integer N.
const size_t StringDedup::Config::good_sizes[] = {
503, 751, 1009, 1511, 2003, 3001, 4001, 6007, 8009, 12007, 16001, 24001,
32003, 48017, 64007, 96001, 128021, 192007, 256019, 384001, 512009, 768013,
1024021, 1536011, 2048003, 3072001, 4096013, 6144001, 8192003, 12288011,
16384001, 24576001, 32768011, 49152001, 65536043, 98304053,
131072003, 196608007, 262144009, 393216007, 524288057, 786432001,
1048576019, 1572864001 };
const size_t StringDedup::Config::min_good_size = good_sizes[0];
const size_t StringDedup::Config::max_good_size = good_sizes[ARRAY_SIZE(good_sizes) - 1];
size_t StringDedup::Config::good_size(size_t n) {
size_t result = good_sizes[ARRAY_SIZE(good_sizes) - 1];
for (size_t i = 0; i < ARRAY_SIZE(good_sizes); ++i) {
if (n <= good_sizes[i]) {
result = good_sizes[i];
break;
}
}
return result;
}
size_t StringDedup::Config::grow_threshold(size_t table_size) {
return (table_size < max_good_size) ?
static_cast<size_t>(table_size * _load_factor_for_growth) :
SIZE_MAX;
}
size_t StringDedup::Config::shrink_threshold(size_t table_size) {
return (table_size > min_good_size) ?
static_cast<size_t>(table_size * _load_factor_for_shrink) :
0;
}
bool StringDedup::Config::should_grow_table(size_t table_size, size_t entry_count) {
return entry_count > grow_threshold(table_size);
}
bool StringDedup::Config::should_shrink_table(size_t table_size, size_t entry_count) {
return entry_count < shrink_threshold(table_size);
}
size_t StringDedup::Config::desired_table_size(size_t entry_count) {
return good_size(static_cast<size_t>(entry_count / _load_factor_target));
}
bool StringDedup::Config::ergo_initialize() {
if (!UseStringDeduplication) {
return true;
} else if (!UseG1GC && !UseShenandoahGC) {
// String deduplication requested but not supported by the selected GC.
// Warn and force disable, but don't error except in debug build with
// incorrect default.
assert(!FLAG_IS_DEFAULT(UseStringDeduplication),
"Enabled by default for GC that doesn't support it");
log_warning(stringdedup)("String Deduplication disabled: "
"not supported by selected GC");
FLAG_SET_ERGO(UseStringDeduplication, false);
return true;
}
// UseStringDeduplication is enabled. Check parameters. These checks are
// in addition to any range or constraint checks directly associated with
// the parameters.
bool result = true;
// ShrinkTableLoad <= TargetTableLoad <= GrowTableLoad.
if (StringDeduplicationShrinkTableLoad > StringDeduplicationTargetTableLoad) {
JVMFlag::printError(true,
"StringDeduplicationShrinkTableLoad (%f) must not exceed "
"StringDeduplicationTargetTableLoad (%f)",
StringDeduplicationShrinkTableLoad,
StringDeduplicationTargetTableLoad);
result = false;
}
if (StringDeduplicationTargetTableLoad > StringDeduplicationGrowTableLoad) {
JVMFlag::printError(true,
"StringDeduplicationTargetTableLoad (%f) must not exceed "
"StringDeduplicationGrowTableLoad (%f)",
StringDeduplicationTargetTableLoad,
StringDeduplicationGrowTableLoad);
result = false;
}
return result;
}
void StringDedup::Config::initialize() {
_initial_table_size = good_size(StringDeduplicationInitialTableSize);
_age_threshold = StringDeduplicationAgeThreshold;
_load_factor_for_growth = StringDeduplicationGrowTableLoad;
_load_factor_for_shrink = StringDeduplicationShrinkTableLoad;
_load_factor_target = StringDeduplicationTargetTableLoad;
_minimum_dead_for_cleanup = StringDeduplicationCleanupDeadMinimum;
_dead_factor_for_cleanup = percent_of(StringDeduplicationCleanupDeadPercent, 100);
_hash_seed = initial_hash_seed();
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2021, 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_STRINGDEDUPCONFIG_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPCONFIG_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "memory/allStatic.hpp"
#include "utilities/globalDefinitions.hpp"
// Provides access to canonicalized configuration parameter values. This
// class captures the various StringDeduplicationXXX command line option
// values, massages them, and provides error checking support.
class StringDedup::Config : AllStatic {
static size_t _initial_table_size;
static int _age_threshold;
static double _load_factor_for_growth;
static double _load_factor_for_shrink;
static double _load_factor_target;
static size_t _minimum_dead_for_cleanup;
static double _dead_factor_for_cleanup;
static uint64_t _hash_seed;
static const size_t good_sizes[];
static const size_t min_good_size;
static const size_t max_good_size;
static size_t good_size(size_t n);
public:
// Perform ergonomic adjustments and error checking.
// Returns true on success, false if some error check failed.
static bool ergo_initialize();
static void initialize();
static size_t initial_table_size();
static int age_threshold();
static uint64_t hash_seed();
static size_t grow_threshold(size_t table_size);
static size_t shrink_threshold(size_t table_size);
static bool should_grow_table(size_t table_size, size_t entry_count);
static bool should_shrink_table(size_t table_size, size_t entry_count);
static size_t desired_table_size(size_t entry_count);
static bool should_cleanup_table(size_t entry_count, size_t dead_count);
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPCONFIG_HPP

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2021, 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 "classfile/javaClasses.inline.hpp"
#include "classfile/stringTable.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageParState.inline.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupProcessor.hpp"
#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/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;
StringDedup::StorageUse* StringDedup::Processor::_storage_for_processing = nullptr;
void StringDedup::Processor::initialize_storage() {
assert(_storages[0] == nullptr, "storage already created");
assert(_storages[1] == nullptr, "storage already created");
assert(_storage_for_requests == nullptr, "storage already created");
assert(_storage_for_processing == nullptr, "storage already created");
_storages[0] = OopStorageSet::create_weak("StringDedup Requests0 Weak", mtStringDedup);
_storages[1] = OopStorageSet::create_weak("StringDedup Requests1 Weak", mtStringDedup);
_storage_for_requests = new StorageUse(_storages[0]);
_storage_for_processing = new StorageUse(_storages[1]);
}
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()) {
MonitorLocker ml(StringDedup_lock, Mutex::_no_safepoint_check_flag);
OopStorage* storage = Atomic::load(&_storage_for_requests)->storage();
while (!should_terminate() &&
(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();
}
// 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");
MonitorLocker ml(StringDedup_lock, Mutex::_no_safepoint_check_flag);
while (_storage_for_processing->is_used_acquire() && !should_terminate()) {
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::cleanup_table(SuspendibleThreadSetJoiner* joiner,
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;
}
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];
void release_ref(oop* ref) {
assert(_release_index < ARRAY_SIZE(_bulk_release), "invariant");
NativeAccess<ON_PHANTOM_OOP_REF>::oop_store(ref, nullptr);
_bulk_release[_release_index++] = ref;
if (_release_index == ARRAY_SIZE(_bulk_release)) {
_storage->release(_bulk_release, _release_index);
_release_index = 0;
}
}
public:
ProcessRequest(OopStorage* storage, SuspendibleThreadSetJoiner* joiner) :
_storage(storage),
_joiner(joiner),
_release_index(0),
_bulk_release()
{}
~ProcessRequest() {
_storage->release(_bulk_release, _release_index);
}
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();
}
}
}
}
};
void StringDedup::Processor::process_requests(SuspendibleThreadSetJoiner* joiner) const {
OopStorage::ParState<true, false> par_state{_storage_for_processing->storage(), 1};
ProcessRequest processor{_storage_for_processing->storage(), joiner};
par_state.oops_do(&processor);
}
void StringDedup::Processor::run_service() {
while (!should_terminate()) {
_cur_stat.report_idle_start();
if (!wait_for_requests()) {
assert(should_terminate(), "invariant");
break;
}
SuspendibleThreadSetJoiner sts_joiner{};
if (should_terminate()) break;
_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();
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);
if (log_is_enabled(Debug, stringdedup)) {
_cur_stat.log_statistics(false);
_total_stat.log_statistics(true);
Table::log_statistics();
}
_cur_stat = Stat{};
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2021, 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_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 "utilities/macros.hpp"
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 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 {
Processor();
~Processor() = default;
NONCOPYABLE(Processor);
static OopStorage* _storages[2];
static StorageUse* volatile _storage_for_requests;
static StorageUse* _storage_for_processing;
// Returns !should_terminate();
bool wait_for_requests() const;
// Yield if requested. Returns !should_terminate() after possible yield.
bool yield_or_continue(SuspendibleThreadSetJoiner* joiner, Stat::Phase phase) const;
class ProcessRequest;
void process_requests(SuspendibleThreadSetJoiner* joiner) const;
void cleanup_table(SuspendibleThreadSetJoiner* joiner, 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();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPPROCESSOR_HPP

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2018, 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/stringDedupQueue.hpp"
#include "runtime/atomic.hpp"
StringDedupQueue* StringDedupQueue::_queue = NULL;
volatile size_t StringDedupQueue::_claimed_index = 0;
size_t StringDedupQueue::claim() {
return Atomic::fetch_and_add(&_claimed_index, 1u);
}
void StringDedupQueue::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl) {
size_t claimed_queue = claim();
while (claimed_queue < queue()->num_queues()) {
queue()->unlink_or_oops_do_impl(cl, claimed_queue);
claimed_queue = claim();
}
}
void StringDedupQueue::print_statistics() {
queue()->print_statistics_impl();
}
void StringDedupQueue::verify() {
queue()->verify_impl();
}
StringDedupQueue* const StringDedupQueue::queue() {
assert(_queue != NULL, "Not yet initialized");
return _queue;
}
void StringDedupQueue::gc_prologue() {
_claimed_index = 0;
}
void StringDedupQueue::gc_epilogue() {
assert(_claimed_index >= queue()->num_queues() || _claimed_index == 0, "All or nothing");
}

View File

@ -1,112 +0,0 @@
/*
* Copyright (c) 2018, 2019, 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_STRINGDEDUPQUEUE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
class StringDedupUnlinkOrOopsDoClosure;
//
// The deduplication queue acts as the communication channel between mark/evacuation
// phase and the concurrent deduplication phase. Deduplication candidates
// found during mark/evacuation are placed on this queue for later processing in the
// deduplication thread. A queue entry is an oop pointing to a String object (as opposed
// to entries in the deduplication hashtable which points to character arrays).
//
// While users of the queue treat it as a single queue, it is implemented as a set of
// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue
// operations by the GC workers.
//
// The oops in the queue are treated as weak pointers, meaning the objects they point to
// can become unreachable and pruned (cleared) before being popped by the deduplication
// thread.
//
// Pushing to the queue is thread safe (this relies on each thread using a unique worker
// id). Popping from the queue is NOT thread safe and can only be done by the deduplication
// thread outside a safepoint.
//
class StringDedupQueue : public CHeapObj<mtGC> {
private:
static StringDedupQueue* _queue;
static volatile size_t _claimed_index;
public:
template <typename Q>
static void create();
// Blocks and waits for the queue to become non-empty.
static inline void wait();
// Wakes up any thread blocked waiting for the queue to become non-empty.
static inline void cancel_wait();
// Pushes a deduplication candidate onto a specific GC worker queue.
static inline void push(uint worker_id, oop java_string);
// Pops a deduplication candidate from any queue, returns NULL if
// all queues are empty.
static inline oop pop();
static void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl);
static void print_statistics();
static void verify();
// GC support
static void gc_prologue();
static void gc_epilogue();
protected:
static StringDedupQueue* const queue();
// Queue interface.
// Blocks and waits for the queue to become non-empty.
virtual void wait_impl() = 0;
// Wakes up any thread blocked waiting for the queue to become non-empty.
virtual void cancel_wait_impl() = 0;
// Pushes a deduplication candidate onto a specific GC worker queue.
virtual void push_impl(uint worker_id, oop java_string) = 0;
// Pops a deduplication candidate from any queue, returns NULL if
// all queues are empty.
virtual oop pop_impl() = 0;
virtual void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) = 0;
virtual void print_statistics_impl() = 0;
virtual void verify_impl() = 0;
virtual size_t num_queues() const = 0;
static size_t claim();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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,51 +25,77 @@
#include "precompiled.hpp"
#include "gc/shared/stringdedup/stringDedupStat.hpp"
#include "logging/log.hpp"
#include "utilities/globalDefinitions.hpp"
StringDedupStat::StringDedupStat() :
StringDedup::Stat::Stat() :
_inspected(0),
_skipped(0),
_hashed(0),
_known(0),
_known_shared(0),
_new(0),
_new_bytes(0),
_deduped(0),
_deduped_bytes(0),
_replaced(0),
_deleted(0),
_skipped_dead(0),
_skipped_incomplete(0),
_skipped_shared(0),
_concurrent(0),
_idle(0),
_exec(0),
_process(0),
_resize_table(0),
_cleanup_table(0),
_block(0),
_start_concurrent(0.0),
_end_concurrent(0.0),
_start_phase(0.0),
_idle_elapsed(0.0),
_exec_elapsed(0.0),
_block_elapsed(0.0) {
_concurrent_start(),
_concurrent_elapsed(),
_phase_start(),
_idle_elapsed(),
_process_elapsed(),
_resize_table_elapsed(),
_cleanup_table_elapsed(),
_block_elapsed() {
}
void StringDedupStat::add(const StringDedupStat* const stat) {
void StringDedup::Stat::add(const Stat* const stat) {
_inspected += stat->_inspected;
_skipped += stat->_skipped;
_hashed += stat->_hashed;
_known += stat->_known;
_known_shared += stat->_known_shared;
_new += stat->_new;
_new_bytes += stat->_new_bytes;
_deduped += stat->_deduped;
_deduped_bytes += stat->_deduped_bytes;
_replaced += stat->_replaced;
_deleted += stat->_deleted;
_skipped_dead += stat->_skipped_dead;
_skipped_incomplete += stat->_skipped_incomplete;
_skipped_shared += stat->_skipped_shared;
_concurrent += stat->_concurrent;
_idle += stat->_idle;
_exec += stat->_exec;
_process += stat->_process;
_resize_table += stat->_resize_table;
_cleanup_table += stat->_cleanup_table;
_block += stat->_block;
_concurrent_elapsed += stat->_concurrent_elapsed;
_idle_elapsed += stat->_idle_elapsed;
_exec_elapsed += stat->_exec_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;
}
void StringDedupStat::print_start(const StringDedupStat* last_stat) {
log_info(gc, stringdedup)(
"Concurrent String Deduplication (" STRDEDUP_TIME_FORMAT ")",
STRDEDUP_TIME_PARAM(last_stat->_start_concurrent));
// Support for log output formating
#define STRDEDUP_PERCENT_FORMAT "%5.1f%%"
#define STRDEDUP_PERCENT_FORMAT_NS "%.1f%%"
#define STRDEDUP_BYTES_FORMAT "%8.1f%s"
#define STRDEDUP_BYTES_FORMAT_NS "%.1f%s"
#define STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes))
#define STRDEDUP_ELAPSED_FORMAT_MS "%.3fms"
static double strdedup_elapsed_param_ms(Tickspan t) {
return t.seconds() * MILLIUNITS;
}
void StringDedupStat::print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat) {
void StringDedup::Stat::log_summary(const Stat* last_stat, const Stat* total_stat) {
double total_deduped_bytes_percent = 0.0;
if (total_stat->_new_bytes > 0) {
@ -77,76 +103,159 @@ void StringDedupStat::print_end(const StringDedupStat* last_stat, const StringDe
total_deduped_bytes_percent = percent_of(total_stat->_deduped_bytes, total_stat->_new_bytes);
}
log_info(gc, stringdedup)(
log_info(stringdedup)(
"Concurrent String Deduplication "
STRDEDUP_BYTES_FORMAT_NS "->" STRDEDUP_BYTES_FORMAT_NS "(" STRDEDUP_BYTES_FORMAT_NS ") "
"avg " STRDEDUP_PERCENT_FORMAT_NS " "
"(" STRDEDUP_TIME_FORMAT ", " STRDEDUP_TIME_FORMAT ") " STRDEDUP_TIME_FORMAT_MS,
STRDEDUP_BYTES_PARAM(last_stat->_new_bytes),
STRDEDUP_BYTES_PARAM(last_stat->_new_bytes - last_stat->_deduped_bytes),
STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes),
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (new), "
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (deduped), "
"avg " STRDEDUP_PERCENT_FORMAT_NS ", "
STRDEDUP_ELAPSED_FORMAT_MS " of " STRDEDUP_ELAPSED_FORMAT_MS,
last_stat->_new, STRDEDUP_BYTES_PARAM(last_stat->_new_bytes),
last_stat->_deduped, STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes),
total_deduped_bytes_percent,
STRDEDUP_TIME_PARAM(last_stat->_start_concurrent),
STRDEDUP_TIME_PARAM(last_stat->_end_concurrent),
STRDEDUP_TIME_PARAM_MS(last_stat->_exec_elapsed));
strdedup_elapsed_param_ms(last_stat->_process_elapsed),
strdedup_elapsed_param_ms(last_stat->_concurrent_elapsed));
}
void StringDedupStat::reset() {
_inspected = 0;
_skipped = 0;
_hashed = 0;
_known = 0;
_new = 0;
_new_bytes = 0;
_deduped = 0;
_deduped_bytes = 0;
_idle = 0;
_exec = 0;
_block = 0;
_start_concurrent = 0.0;
_end_concurrent = 0.0;
_start_phase = 0.0;
_idle_elapsed = 0.0;
_exec_elapsed = 0.0;
_block_elapsed = 0.0;
void StringDedup::Stat::report_concurrent_start() {
log_debug(stringdedup, phases, start)("Concurrent start");
_concurrent_start = Ticks::now();
_concurrent++;
}
void StringDedupStat::print_statistics(bool total) const {
double skipped_percent = percent_of(_skipped, _inspected);
double hashed_percent = percent_of(_hashed, _inspected);
double known_percent = percent_of(_known, _inspected);
double new_percent = percent_of(_new, _inspected);
double deduped_percent = percent_of(_deduped, _new);
double deduped_bytes_percent = percent_of(_deduped_bytes, _new_bytes);
/*
double deduped_young_percent = percent_of(stat._deduped_young, stat._deduped);
double deduped_young_bytes_percent = percent_of(stat._deduped_young_bytes, stat._deduped_bytes);
double deduped_old_percent = percent_of(stat._deduped_old, stat._deduped);
double deduped_old_bytes_percent = percent_of(stat._deduped_old_bytes, stat._deduped_bytes);
*/
if (total) {
log_debug(gc, stringdedup)(
" Total Exec: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS
", Idle: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS
", Blocked: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS,
_exec, STRDEDUP_TIME_PARAM_MS(_exec_elapsed),
_idle, STRDEDUP_TIME_PARAM_MS(_idle_elapsed),
_block, STRDEDUP_TIME_PARAM_MS(_block_elapsed));
} else {
log_debug(gc, stringdedup)(
" Last Exec: " STRDEDUP_TIME_FORMAT_MS
", Idle: " STRDEDUP_TIME_FORMAT_MS
", Blocked: " UINTX_FORMAT "/" STRDEDUP_TIME_FORMAT_MS,
STRDEDUP_TIME_PARAM_MS(_exec_elapsed),
STRDEDUP_TIME_PARAM_MS(_idle_elapsed),
_block, STRDEDUP_TIME_PARAM_MS(_block_elapsed));
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_phase_start(const char* phase) {
log_debug(stringdedup, phases, start)("%s start", phase);
_phase_start = Ticks::now();
}
void StringDedup::Stat::report_phase_end(const char* phase, Tickspan* elapsed) {
*elapsed += Ticks::now() - _phase_start;
log_debug(stringdedup, phases)("%s end: " STRDEDUP_ELAPSED_FORMAT_MS,
phase, strdedup_elapsed_param_ms(*elapsed));
}
void StringDedup::Stat::report_idle_start() {
report_phase_start("Idle");
_idle++;
}
void StringDedup::Stat::report_idle_end() {
report_phase_end("Idle", &_idle_elapsed);
}
void StringDedup::Stat::report_process_start() {
report_phase_start("Process");
_process++;
}
void StringDedup::Stat::report_process_pause() {
_process_elapsed += (Ticks::now() - _phase_start);
log_debug(stringdedup, phases)("Process paused");
}
void StringDedup::Stat::report_process_resume() {
log_debug(stringdedup, phases)("Process resume");
_phase_start = Ticks::now();
}
void StringDedup::Stat::report_process_end() {
report_phase_end("Process", &_process_elapsed);
}
void StringDedup::Stat::report_resize_table_start(size_t new_bucket_count,
size_t old_bucket_count,
size_t entry_count) {
_phase_start = Ticks::now();
++_resize_table;
log_debug(stringdedup, phases, start)
("Resize Table: %zu -> %zu (%zu)",
old_bucket_count, new_bucket_count, entry_count);
}
void StringDedup::Stat::report_resize_table_end() {
report_phase_end("Resize Table", &_resize_table_elapsed);
}
void StringDedup::Stat::report_cleanup_table_start(size_t entry_count,
size_t dead_count) {
log_debug(stringdedup, phases, start)
("Cleanup Table: %zu / %zu -> %zu",
dead_count, entry_count, (entry_count - dead_count));
_phase_start = Ticks::now();
_cleanup_table++;
}
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;
}
log_debug(gc, stringdedup)(" Inspected: " STRDEDUP_OBJECTS_FORMAT, _inspected);
log_debug(gc, stringdedup)(" Skipped: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _skipped, skipped_percent);
log_debug(gc, stringdedup)(" Hashed: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _hashed, hashed_percent);
log_debug(gc, stringdedup)(" Known: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")", _known, known_percent);
log_debug(gc, stringdedup)(" New: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT,
_new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes));
log_debug(gc, stringdedup)(" Deduplicated: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")",
_deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent);
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,
prefix,
_process, strdedup_elapsed_param_ms(_process_elapsed),
_idle, strdedup_elapsed_param_ms(_idle_elapsed),
_block, strdedup_elapsed_param_ms(_block_elapsed));
if (_resize_table > 0) {
log_debug(stringdedup)(
" %s Resize Table: %zu/" STRDEDUP_ELAPSED_FORMAT_MS,
prefix, _resize_table, strdedup_elapsed_param_ms(_resize_table_elapsed));
}
if (_cleanup_table > 0) {
log_debug(stringdedup)(
" %s Cleanup Table: %zu/" STRDEDUP_ELAPSED_FORMAT_MS,
prefix, _cleanup_table, strdedup_elapsed_param_ms(_cleanup_table_elapsed));
}
}
void StringDedup::Stat::log_statistics(bool total) const {
double known_percent = percent_of(_known, _inspected);
double known_shared_percent = percent_of(_known_shared, _inspected);
double new_percent = percent_of(_new, _inspected);
double deduped_percent = percent_of(_deduped, _inspected);
double deduped_bytes_percent = percent_of(_deduped_bytes, _new_bytes);
double replaced_percent = percent_of(_replaced, _new);
double deleted_percent = percent_of(_deleted, _new);
log_times(total ? "Total" : "Last");
log_debug(stringdedup)(" Inspected: %12zu", _inspected);
log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
log_debug(stringdedup)(" New: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
_new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes));
log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
_deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent);
log_debug(stringdedup)(" Skipped: %zu (dead), %zu (incomplete), %zu (shared)",
_skipped_dead, _skipped_incomplete, _skipped_shared);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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,114 +25,144 @@
#ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP
#include "memory/allocation.hpp"
#include "runtime/os.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ticks.hpp"
// Macros for GC log output formating
#define STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12)
#define STRDEDUP_TIME_FORMAT "%.3fs"
#define STRDEDUP_TIME_PARAM(time) (time)
#define STRDEDUP_TIME_FORMAT_MS "%.3fms"
#define STRDEDUP_TIME_PARAM_MS(time) ((time) * MILLIUNITS)
#define STRDEDUP_PERCENT_FORMAT "%5.1f%%"
#define STRDEDUP_PERCENT_FORMAT_NS "%.1f%%"
#define STRDEDUP_BYTES_FORMAT "%8.1f%s"
#define STRDEDUP_BYTES_FORMAT_NS "%.1f%s"
#define STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes))
// Deduplication statistics.
//
// 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
};
//
// Statistics gathered by the deduplication thread.
//
class StringDedupStat : public CHeapObj<mtGC> {
protected:
private:
// Counters
uintx _inspected;
uintx _skipped;
uintx _hashed;
uintx _known;
uintx _new;
uintx _new_bytes;
uintx _deduped;
uintx _deduped_bytes;
uintx _idle;
uintx _exec;
uintx _block;
size_t _inspected;
size_t _known;
size_t _known_shared;
size_t _new;
size_t _new_bytes;
size_t _deduped;
size_t _deduped_bytes;
size_t _replaced;
size_t _deleted;
size_t _skipped_dead;
size_t _skipped_incomplete;
size_t _skipped_shared;
// Phase counters for deduplication thread
size_t _concurrent;
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
double _start_concurrent;
double _end_concurrent;
double _start_phase;
double _idle_elapsed;
double _exec_elapsed;
double _block_elapsed;
Ticks _concurrent_start;
Tickspan _concurrent_elapsed;
Ticks _phase_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;
public:
StringDedupStat();
Stat();
// Track number of strings looked up.
void inc_inspected() {
_inspected++;
}
void inc_skipped() {
_skipped++;
// Track number of requests skipped because string died.
void inc_skipped_dead() {
_skipped_dead++;
}
void inc_hashed() {
_hashed++;
// Track number of requests skipped because string was incomplete.
void inc_skipped_incomplete() {
_skipped_incomplete++;
}
// Track number of shared strings skipped because of a previously
// installed equivalent entry.
void inc_skipped_shared() {
_skipped_shared++;
}
// Track number of inspected strings already present.
void inc_known() {
_known++;
}
void inc_new(uintx bytes) {
// Track number of inspected strings found in the shared StringTable.
void inc_known_shared() {
_known_shared++;
}
// Track number of inspected strings added and accumulated size.
void inc_new(size_t bytes) {
_new++;
_new_bytes += bytes;
}
virtual void deduped(oop obj, uintx bytes) {
// Track number of inspected strings dedup'ed and accumulated savings.
void inc_deduped(size_t bytes) {
_deduped++;
_deduped_bytes += bytes;
}
void mark_idle() {
_start_phase = os::elapsedTime();
_idle++;
// Track number of interned strings replacing existing strings.
void inc_replaced() {
_replaced++;
}
void mark_exec() {
double now = os::elapsedTime();
_idle_elapsed = now - _start_phase;
_start_phase = now;
_start_concurrent = now;
_exec++;
// Track number of strings removed from table.
void inc_deleted() {
_deleted++;
}
void mark_block() {
double now = os::elapsedTime();
_exec_elapsed += now - _start_phase;
_start_phase = now;
_block++;
}
void report_idle_start();
void report_idle_end();
void mark_unblock() {
double now = os::elapsedTime();
_block_elapsed += now - _start_phase;
_start_phase = now;
}
void report_process_start();
void report_process_pause();
void report_process_resume();
void report_process_end();
void mark_done() {
double now = os::elapsedTime();
_exec_elapsed += now - _start_phase;
_end_concurrent = now;
}
void report_resize_table_start(size_t new_bucket_count,
size_t old_bucket_count,
size_t entry_count);
void report_resize_table_end();
virtual void reset();
virtual void add(const StringDedupStat* const stat);
virtual void print_statistics(bool total) const;
void report_cleanup_table_start(size_t entry_count, size_t dead_count);
void report_cleanup_table_end();
static void print_start(const StringDedupStat* last_stat);
static void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat);
void report_concurrent_start();
void report_concurrent_end();
void block_phase(Phase phase);
void unblock_phase();
void add(const Stat* const stat);
void log_statistics(bool total) const;
static void log_summary(const Stat* last_stat, const Stat* total_stat);
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 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
@ -22,33 +22,31 @@
*
*/
#ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP
#include "precompiled.hpp"
#include "gc/shared/stringdedup/stringDedupStorageUse.hpp"
#include "runtime/atomic.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalCounter.inline.hpp"
#include "utilities/globalDefinitions.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
StringDedup::StorageUse::StorageUse(OopStorage* storage) :
_storage(storage), _use_count(0)
{}
template <typename Q>
void StringDedupQueue::create() {
assert(StringDedup::is_enabled(), "Must be enabled");
assert(_queue == NULL, "Can have only one queue");
_queue = new Q;
bool StringDedup::StorageUse::is_used_acquire() const {
return Atomic::load_acquire(&_use_count) > 0;
}
void StringDedupQueue::wait() {
queue()->wait_impl();
StringDedup::StorageUse*
StringDedup::StorageUse::obtain(StorageUse* volatile* ptr) {
GlobalCounter::CriticalSection cs(Thread::current());
StorageUse* storage = Atomic::load(ptr);
Atomic::inc(&storage->_use_count);
return storage;
}
void StringDedupQueue::cancel_wait() {
queue()->cancel_wait_impl();
void StringDedup::StorageUse::relinquish() {
size_t result = Atomic::sub(&_use_count, size_t(1));
assert(result != SIZE_MAX, "use count underflow");
}
void StringDedupQueue::push(uint worker_id, oop java_string) {
queue()->push_impl(worker_id, java_string);
}
oop StringDedupQueue::pop() {
return queue()->pop_impl();
}
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2021, 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_STRINGDEDUPSTORAGEUSE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTORAGEUSE_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
class OopStorage;
// Manage access to one of the OopStorage objects used for requests.
class StringDedup::StorageUse : public CHeapObj<mtStringDedup> {
OopStorage* const _storage;
volatile size_t _use_count;
NONCOPYABLE(StorageUse);
public:
explicit StorageUse(OopStorage* storage);
OopStorage* storage() const { return _storage; }
// Return true if the storage is currently in use for registering requests.
bool is_used_acquire() const;
// Get the current requests object, and increment its in-use count.
static StorageUse* obtain(StorageUse* volatile* ptr);
// Discard a prior "obtain" request, decrementing the in-use count, and
// permitting the deduplication thread to start processing if needed.
void relinquish();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTORAGEUSE_HPP

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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,229 +25,125 @@
#ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
#include "memory/allStatic.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupStat.hpp"
#include "runtime/mutexLocker.hpp"
#include "oops/typeArrayOop.hpp"
#include "oops/weakHandle.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
class StringDedupEntryCache;
class StringDedupUnlinkOrOopsDoClosure;
class OopStorage;
// Provides deduplication. This class keeps track of all the unique byte
// arrays used by deduplicated String objects.
//
// Table entry in the deduplication hashtable. Points weakly to the
// character array. Can be chained in a linked list in case of hash
// collisions or when placed in a freelist in the entry cache.
// The arrays are in a hashtable, hashed using the bytes in the array. The
// references to the arrays by the hashtable are weak, allowing arrays that
// become unreachable to be collected and their entries pruned from the
// table. The hashtable is dynamically resized to accommodate the current
// number of hashtable entries. There are several command line options
// controlling the growth or shrinkage of the hashtable.
//
class StringDedupEntry : public CHeapObj<mtGC> {
// Operations on the table are not thread-safe. Only the deduplication
// thread calls most of the operations on the table. The only exception is
// the GC dead object count notification and the management of its state.
//
// The table supports resizing and removal of entries for byte arrays that
// have become unreferenced. These operations are performed by the
// deduplication thread, in a series of small incremental steps. This
// prevents these potentially long running operations from long blockage of
// safepoints or concurrent deduplication requests from the StringTable.
//
// As a space optimization, when shared StringTable entries exist the shared
// part of the StringTable is also used as a source for byte arrays. This
// permits deduplication of strings against those shared entries without
// recording them in this table too.
class StringDedup::Table : AllStatic {
private:
StringDedupEntry* _next;
unsigned int _hash;
bool _latin1;
typeArrayOop _obj;
class Bucket;
class CleanupState;
class Resizer;
class Cleaner;
enum class DeadState;
// Values in the table are weak references to jbyte[] Java objects. The
// String's coder isn't recorded, even though it affects how String access
// would interpret that array. For the purposes of deduplication we don't
// care about that distinction; two Strings with equivalent arrays but
// different coders can be deduplicated to share a single array. We also
// can't depend on the coder value being correct here, since GC requests
// can provide the deduplication thread with access to a String that is
// incompletely constructed; the value could be set before the coder.
using TableValue = WeakHandle;
// Weak storage for the string data in the table.
static OopStorage* _table_storage;
static Bucket* _buckets;
static size_t _number_of_buckets;
static size_t _number_of_entries;
static size_t _grow_threshold;
static CleanupState* _cleanup_state;
static bool _need_bucket_shrinking;
// These are always written while holding StringDedup_lock, but may be
// read by the dedup thread without holding the lock lock.
static volatile size_t _dead_count;
static volatile DeadState _dead_state;
static uint compute_hash(typeArrayOop obj);
static size_t hash_to_index(uint hash_code);
static void add(TableValue tv, uint hash_code);
static TableValue find(typeArrayOop obj, uint hash_code);
static void install(typeArrayOop obj, uint hash_code);
static bool deduplicate_if_permitted(oop java_string, typeArrayOop value);
static bool try_deduplicate_shared(oop java_string);
static bool try_deduplicate_found_shared(oop java_string, oop found);
static Bucket* make_buckets(size_t number_of_buckets, size_t reserve = 0);
static void free_buckets(Bucket* buckets, size_t number_of_buckets);
static bool start_resizer(bool grow_only, size_t number_of_entries);
static bool start_cleaner(size_t number_of_entries, size_t dead_count);
static void num_dead_callback(size_t num_dead);
static bool is_dead_count_good_acquire();
static void set_dead_state_cleaning();
public:
StringDedupEntry() :
_next(NULL),
_hash(0),
_latin1(false),
_obj(NULL) {
}
static void initialize_storage();
static void initialize();
StringDedupEntry* next() {
return _next;
}
// Deduplicate java_string. If the table already contains the string's
// data array, replace the string's data array with the one in the table.
// Otherwise, add the string's data array to the table.
static void deduplicate(oop java_string);
StringDedupEntry** next_addr() {
return &_next;
}
// Returns true if table needs to grow.
static bool is_grow_needed();
void set_next(StringDedupEntry* next) {
_next = next;
}
// Returns true if there are enough dead entries to need cleanup.
static bool is_dead_entry_removal_needed();
unsigned int hash() {
return _hash;
}
// If cleanup (resizing or removing dead entries) is needed or force
// is true, setup cleanup state and return true. If result is true,
// the caller must eventually call cleanup_end.
// precondition: no cleanup is in progress.
static bool cleanup_start_if_needed(bool grow_only, bool force);
void set_hash(unsigned int hash) {
_hash = hash;
}
// Perform some cleanup work. Returns true if any progress was made,
// false if there is no further work to do.
// precondition: a cleanup is in progress.
static bool cleanup_step();
bool latin1() {
return _latin1;
}
// Record the cleanup complete and cleanup state.
// precondition: a cleanup is in progress.
static void cleanup_end();
void set_latin1(bool latin1) {
_latin1 = latin1;
}
// Return the phase kind for the cleanup being performed.
// precondition: a cleanup is in progress.
static Stat::Phase cleanup_phase();
typeArrayOop obj() {
return _obj;
}
typeArrayOop* obj_addr() {
return &_obj;
}
void set_obj(typeArrayOop obj) {
_obj = obj;
}
};
//
// The deduplication hashtable keeps track of all unique character arrays used
// by String objects. Each table entry weakly points to an character array, allowing
// otherwise unreachable character arrays to be declared dead and pruned from the
// table.
//
// The table is dynamically resized to accommodate the current number of table entries.
// The table has hash buckets with chains for hash collision. If the average chain
// length goes above or below given thresholds the table grows or shrinks accordingly.
//
// The table is also dynamically rehashed (using a new hash seed) if it becomes severely
// unbalanced, i.e., a hash chain is significantly longer than average.
//
// All access to the table is protected by the StringDedupTable_lock, except under
// safepoints in which case GC workers are allowed to access a table partitions they
// have claimed without first acquiring the lock. Note however, that this applies only
// the table partition (i.e. a range of elements in _buckets), not other parts of the
// table such as the _entries field, statistics counters, etc.
//
class StringDedupTable : public CHeapObj<mtGC> {
private:
// The currently active hashtable instance. Only modified when
// the table is resizes or rehashed.
static StringDedupTable* _table;
// Cache for reuse and fast alloc/free of table entries.
static StringDedupEntryCache* _entry_cache;
StringDedupEntry** _buckets;
size_t _size;
volatile uintx _entries;
uintx _shrink_threshold;
uintx _grow_threshold;
bool _rehash_needed;
// The hash seed also dictates which hash function to use. A
// zero hash seed means we will use the Java compatible hash
// function (which doesn't use a seed), and a non-zero hash
// seed means we use the murmur3 hash function.
uint64_t _hash_seed;
// Constants governing table resize/rehash/cache.
static const size_t _min_size;
static const size_t _max_size;
static const double _grow_load_factor;
static const double _shrink_load_factor;
static const uintx _rehash_multiple;
static const uintx _rehash_threshold;
static const double _max_cache_factor;
// Table statistics, only used for logging.
static uintx _entries_added;
static volatile uintx _entries_removed;
static uintx _resize_count;
static uintx _rehash_count;
static volatile size_t _claimed_index;
static StringDedupTable* _resized_table;
static StringDedupTable* _rehashed_table;
StringDedupTable(size_t size, uint64_t hash_seed = 0);
~StringDedupTable();
// Returns the hash bucket at the given index.
StringDedupEntry** bucket(size_t index) {
return _buckets + index;
}
// Returns the hash bucket index for the given hash code.
size_t hash_to_index(unsigned int hash) {
return (size_t)hash & (_size - 1);
}
// Adds a new table entry to the given hash bucket.
void add(typeArrayOop value, bool latin1, unsigned int hash, StringDedupEntry** list);
// Removes the given table entry from the table.
void remove(StringDedupEntry** pentry, uint worker_id);
// Transfers a table entry from the current table to the destination table.
void transfer(StringDedupEntry** pentry, StringDedupTable* dest);
// Returns an existing character array in the given hash bucket, or NULL
// if no matching character array exists.
typeArrayOop lookup(typeArrayOop value, bool latin1, unsigned int hash,
StringDedupEntry** list, uintx &count);
// Returns an existing character array in the table, or inserts a new
// table entry if no matching character array exists.
typeArrayOop lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash);
// Thread safe lookup or add of table entry
static typeArrayOop lookup_or_add(typeArrayOop value, bool latin1, unsigned int hash) {
// Protect the table from concurrent access. Also note that this lock
// acts as a fence for _table, which could have been replaced by a new
// instance if the table was resized or rehashed.
MutexLocker ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
return _table->lookup_or_add_inner(value, latin1, hash);
}
// Returns true if the hashtable is currently using a Java compatible
// hash function.
static bool use_java_hash() {
return _table->_hash_seed == 0;
}
// Computes the hash code for the given character array, using the
// currently active hash function and hash seed.
static unsigned int hash_code(typeArrayOop value, bool latin1);
static uintx unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl,
size_t partition_begin,
size_t partition_end,
uint worker_id);
static size_t claim_table_partition(size_t partition_size);
static bool is_resizing();
static bool is_rehashing();
// If a table resize is needed, returns a newly allocated empty
// hashtable of the proper size.
static StringDedupTable* prepare_resize();
// Installs a newly resized table as the currently active table
// and deletes the previously active table.
static void finish_resize(StringDedupTable* resized_table);
// If a table rehash is needed, returns a newly allocated empty
// hashtable and updates the hash seed.
static StringDedupTable* prepare_rehash();
// Transfers rehashed entries from the currently active table into
// the new table. Installs the new table as the currently active table
// and deletes the previously active table.
static void finish_rehash(StringDedupTable* rehashed_table);
public:
static void create();
// Deduplicates the given String object, or adds its backing
// character array to the deduplication hashtable.
static void deduplicate(oop java_string, StringDedupStat* stat);
static void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
static void print_statistics();
static void verify();
// If the table entry cache has grown too large, delete overflowed entries.
static void clean_entry_cache();
// GC support
static void gc_prologue(bool resize_and_rehash_table);
static void gc_epilogue();
static void log_statistics();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP

View File

@ -1,94 +0,0 @@
/*
* Copyright (c) 2014, 2018, 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 "classfile/stringTable.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.inline.hpp"
#include "gc/shared/stringdedup/stringDedupTable.hpp"
#include "gc/shared/stringdedup/stringDedupThread.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "logging/log.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
StringDedupThread* StringDedupThread::_thread = NULL;
StringDedupThread::StringDedupThread() :
ConcurrentGCThread() {
set_name("StrDedup");
create_and_start();
}
StringDedupThread::~StringDedupThread() {
ShouldNotReachHere();
}
StringDedupThread* StringDedupThread::thread() {
assert(_thread != NULL, "String deduplication thread not created");
return _thread;
}
class StringDedupSharedClosure: public OopClosure {
private:
StringDedupStat* _stat;
public:
StringDedupSharedClosure(StringDedupStat* stat) : _stat(stat) {}
virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
virtual void do_oop(oop* p) {
oop java_string = RawAccess<>::oop_load(p);
StringDedupTable::deduplicate(java_string, _stat);
}
};
// The CDS archive does not include the string deduplication table. Only the string
// table is saved in the archive. The shared strings from CDS archive need to be
// added to the string deduplication table before deduplication occurs. That is
// done in the beginning of the StringDedupThread (see StringDedupThread::do_deduplication()).
void StringDedupThread::deduplicate_shared_strings(StringDedupStat* stat) {
StringDedupSharedClosure sharedStringDedup(stat);
StringTable::shared_oops_do(&sharedStringDedup);
}
void StringDedupThread::stop_service() {
StringDedupQueue::cancel_wait();
}
void StringDedupThread::print_start(const StringDedupStat* last_stat) {
StringDedupStat::print_start(last_stat);
}
void StringDedupThread::print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat) {
StringDedupStat::print_end(last_stat, total_stat);
if (log_is_enabled(Debug, gc, stringdedup)) {
last_stat->print_statistics(false);
total_stat->print_statistics(true);
StringDedupTable::print_statistics();
StringDedupQueue::print_statistics();
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2014, 2019, 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/concurrentGCThread.hpp"
#include "gc/shared/stringdedup/stringDedupStat.hpp"
//
// The deduplication thread is where the actual deduplication occurs. It waits for
// deduplication candidates to appear on the deduplication queue, removes them from
// the queue and tries to deduplicate them. It uses the deduplication hashtable to
// find identical, already existing, character arrays on the heap. The thread runs
// concurrently with the Java application but participates in safepoints to allow
// the GC to adjust and unlink oops from the deduplication queue and table.
//
class StringDedupThread: public ConcurrentGCThread {
protected:
static StringDedupThread* _thread;
StringDedupThread();
~StringDedupThread();
void print_start(const StringDedupStat* last_stat);
void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat);
void run_service() { this->do_deduplication(); }
void stop_service();
void deduplicate_shared_strings(StringDedupStat* stat);
protected:
virtual void do_deduplication() = 0;
public:
static StringDedupThread* thread();
};
template <typename S>
class StringDedupThreadImpl : public StringDedupThread {
private:
StringDedupThreadImpl() { }
protected:
void do_deduplication();
public:
static void create();
};
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2018, 2019, 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_INLINE_HPP
#define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/stringdedup/stringDedupQueue.inline.hpp"
#include "gc/shared/stringdedup/stringDedupThread.hpp"
template <typename S>
void StringDedupThreadImpl<S>::do_deduplication() {
S total_stat;
{
// Block safepoints while deduplicating shared strings
SuspendibleThreadSetJoiner sts_join;
deduplicate_shared_strings(&total_stat);
}
// Main loop
for (;;) {
S stat;
stat.mark_idle();
// Wait for the queue to become non-empty
StringDedupQueue::wait();
if (this->should_terminate()) {
break;
}
{
// Include thread in safepoints
SuspendibleThreadSetJoiner sts_join;
stat.mark_exec();
StringDedupStat::print_start(&stat);
// Process the queue
for (;;) {
oop java_string = StringDedupQueue::pop();
if (java_string == NULL) {
break;
}
StringDedupTable::deduplicate(java_string, &stat);
// Safepoint this thread if needed
if (sts_join.should_yield()) {
stat.mark_block();
sts_join.yield();
stat.mark_unblock();
}
}
stat.mark_done();
total_stat.add(&stat);
print_end(&stat, &total_stat);
stat.reset();
}
StringDedupTable::clean_entry_cache();
}
}
template <typename S>
void StringDedupThreadImpl<S>::create() {
assert(_thread == NULL, "One string deduplication thread allowed");
_thread = new StringDedupThreadImpl<S>();
}
#endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP

View File

@ -728,7 +728,6 @@ private:
ShenandoahClassLoaderDataRoots<true /* concurrent */, true /* single thread*/>
_cld_roots;
ShenandoahConcurrentNMethodIterator _nmethod_itr;
ShenandoahConcurrentStringDedupRoots _dedup_roots;
ShenandoahPhaseTimings::Phase _phase;
public:
@ -737,19 +736,14 @@ public:
_vm_roots(phase),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()),
_nmethod_itr(ShenandoahCodeRoots::table()),
_dedup_roots(phase),
_phase(phase) {
if (ShenandoahHeap::heap()->unload_classes()) {
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
_nmethod_itr.nmethods_do_begin();
}
_dedup_roots.prologue();
}
~ShenandoahConcurrentWeakRootsEvacUpdateTask() {
_dedup_roots.epilogue();
if (ShenandoahHeap::heap()->unload_classes()) {
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
_nmethod_itr.nmethods_do_end();
@ -766,11 +760,6 @@ public:
// may race against OopStorage::release() calls.
ShenandoahEvacUpdateCleanupOopStorageRootsClosure cl;
_vm_roots.oops_do(&cl, worker_id);
// String dedup weak roots
ShenandoahForwardedIsAliveClosure is_alive;
ShenandoahEvacuateUpdateMetadataClosure<MO_RELEASE> keep_alive;
_dedup_roots.oops_do(&is_alive, &keep_alive, worker_id);
}
// If we are going to perform concurrent class unloading later on, we need to

View File

@ -388,7 +388,6 @@ jint ShenandoahHeap::initialize() {
_monitoring_support = new ShenandoahMonitoringSupport(this);
_phase_timings = new ShenandoahPhaseTimings(max_workers());
ShenandoahStringDedup::initialize();
ShenandoahCodeRoots::initialize();
if (ShenandoahPacing) {
@ -1785,11 +1784,6 @@ void ShenandoahHeap::stop() {
// Step 3. Wait until GC worker exits normally.
control_thread()->stop();
// Step 4. Stop String Dedup thread if it is active
if (ShenandoahStringDedup::is_enabled()) {
ShenandoahStringDedup::stop();
}
}
void ShenandoahHeap::stw_unload_classes(bool full_gc) {
@ -2288,14 +2282,6 @@ char ShenandoahHeap::gc_state() const {
return _gc_state.raw_value();
}
void ShenandoahHeap::deduplicate_string(oop str) {
assert(java_lang_String::is_instance(str), "invariant");
if (ShenandoahStringDedup::is_enabled()) {
ShenandoahStringDedup::deduplicate(str);
}
}
ShenandoahLiveData* ShenandoahHeap::get_liveness_cache(uint worker_id) {
#ifdef ASSERT
assert(_liveness_cache != NULL, "sanity");

View File

@ -641,8 +641,6 @@ public:
void trash_humongous_region_at(ShenandoahHeapRegion *r);
void deduplicate_string(oop str);
private:
void trash_cset_regions();

View File

@ -36,6 +36,7 @@
ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) :
MetadataVisitingOopIterateClosure(rp),
_stringDedup_requests(),
_queue(q),
_mark_context(ShenandoahHeap::heap()->marking_context()),
_weak(false)

View File

@ -25,6 +25,7 @@
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARK_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHMARK_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/taskTerminator.hpp"
#include "gc/shenandoah/shenandoahOopClosures.hpp"
#include "gc/shenandoah/shenandoahTaskqueue.hpp"
@ -45,7 +46,7 @@ protected:
public:
template<class T, StringDedupMode STRING_DEDUP>
static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak);
static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, StringDedup::Requests* const req, bool weak);
static void clear();

View File

@ -207,6 +207,7 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl,
class ShenandoahSATBBufferClosure : public SATBBufferClosure {
private:
StringDedup::Requests _stringdedup_requests;
ShenandoahObjToScanQueue* _queue;
ShenandoahHeap* _heap;
ShenandoahMarkingContext* const _mark_context;
@ -231,13 +232,13 @@ public:
void do_buffer_impl(void **buffer, size_t size) {
for (size_t i = 0; i < size; ++i) {
oop *p = (oop *) &buffer[i];
ShenandoahMark::mark_through_ref<oop, STRING_DEDUP>(p, _queue, _mark_context, false);
ShenandoahMark::mark_through_ref<oop, STRING_DEDUP>(p, _queue, _mark_context, &_stringdedup_requests, false);
}
}
};
template<class T, StringDedupMode STRING_DEDUP>
inline void ShenandoahMark::mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) {
inline void ShenandoahMark::mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, StringDedup::Requests* const req, bool weak) {
T o = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(o)) {
oop obj = CompressedOops::decode_not_null(o);
@ -258,7 +259,7 @@ inline void ShenandoahMark::mark_through_ref(T* p, ShenandoahObjToScanQueue* q,
if ((STRING_DEDUP == ENQUEUE_DEDUP) && ShenandoahStringDedup::is_candidate(obj)) {
assert(ShenandoahStringDedup::is_enabled(), "Must be enabled");
ShenandoahStringDedup::enqueue_candidate(obj);
req->add(obj);
}
}

View File

@ -25,9 +25,8 @@
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOOPCLOSURES_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHOOPCLOSURES_HPP
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahStrDedupQueue.hpp"
#include "gc/shenandoah/shenandoahTaskqueue.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "memory/iterator.hpp"
@ -40,6 +39,7 @@ enum StringDedupMode {
class ShenandoahMarkRefsSuperClosure : public MetadataVisitingOopIterateClosure {
private:
StringDedup::Requests _stringDedup_requests;
ShenandoahObjToScanQueue* _queue;
ShenandoahMarkingContext* const _mark_context;
bool _weak;

View File

@ -30,7 +30,7 @@
template<class T, StringDedupMode STRING_DEDUP>
inline void ShenandoahMarkRefsSuperClosure::work(T* p) {
ShenandoahMark::mark_through_ref<T, STRING_DEDUP>(p, _queue, _mark_context, _weak);
ShenandoahMark::mark_through_ref<T, STRING_DEDUP>(p, _queue, _mark_context, &_stringDedup_requests, _weak);
}
template<class T, StringDedupMode STRING_DEDUP>

View File

@ -29,7 +29,6 @@
#include "gc/shared/weakProcessor.hpp"
#include "gc/shared/workgroup.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp"
#include "memory/iterator.hpp"
// Perform weak root cleaning at a pause
@ -38,7 +37,6 @@ class ShenandoahParallelWeakRootsCleaningTask : public AbstractGangTask {
protected:
ShenandoahPhaseTimings::Phase const _phase;
WeakProcessor::Task _weak_processing_task;
ShenandoahStringDedupRoots _dedup_roots;
IsAlive* _is_alive;
KeepAlive* _keep_alive;

View File

@ -40,7 +40,6 @@ ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::ShenandoahParallelW
AbstractGangTask("Shenandoah Weak Root Cleaning"),
_phase(phase),
_weak_processing_task(num_workers),
_dedup_roots(phase),
_is_alive(is_alive),
_keep_alive(keep_alive) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
@ -57,7 +56,6 @@ void ShenandoahParallelWeakRootsCleaningTask<IsAlive, KeepAlive>::work(uint work
ShenandoahWorkerTimingsTracker x(_phase, ShenandoahPhaseTimings::VMWeakRoots, worker_id);
_weak_processing_task.work<IsAlive, KeepAlive>(worker_id, _is_alive, _keep_alive);
}
_dedup_roots.oops_do(_is_alive, _keep_alive, worker_id);
}
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPARALLELCLEANING_INLINE_HPP

View File

@ -42,8 +42,6 @@ class outputStream;
f(CNT_PREFIX ## CLDGRoots, DESC_PREFIX "CLDG Roots") \
f(CNT_PREFIX ## CodeCacheUnload, DESC_PREFIX "Unload Code Caches") \
f(CNT_PREFIX ## CLDUnlink, DESC_PREFIX "Unlink CLDs") \
f(CNT_PREFIX ## StringDedupTableRoots, DESC_PREFIX "Dedup Table Roots") \
f(CNT_PREFIX ## StringDedupQueueRoots, DESC_PREFIX "Dedup Queue Roots") \
f(CNT_PREFIX ## WeakRefProc, DESC_PREFIX "Weak References") \
f(CNT_PREFIX ## ParallelMark, DESC_PREFIX "Parallel Mark") \
// end

View File

@ -31,7 +31,6 @@
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahStackWatermark.hpp"
#include "gc/shenandoah/shenandoahStringDedup.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/stackWatermarkSet.inline.hpp"
@ -79,62 +78,6 @@ ShenandoahThreadRoots::~ShenandoahThreadRoots() {
Threads::assert_all_threads_claimed();
}
ShenandoahStringDedupRoots::ShenandoahStringDedupRoots(ShenandoahPhaseTimings::Phase phase) : _phase(phase) {
if (ShenandoahStringDedup::is_enabled()) {
StringDedup::gc_prologue(false);
}
}
ShenandoahStringDedupRoots::~ShenandoahStringDedupRoots() {
if (ShenandoahStringDedup::is_enabled()) {
StringDedup::gc_epilogue();
}
}
void ShenandoahStringDedupRoots::oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id) {
if (ShenandoahStringDedup::is_enabled()) {
ShenandoahStringDedup::parallel_oops_do(_phase, is_alive, keep_alive, worker_id);
}
}
ShenandoahConcurrentStringDedupRoots::ShenandoahConcurrentStringDedupRoots(ShenandoahPhaseTimings::Phase phase) :
_phase(phase) {
}
void ShenandoahConcurrentStringDedupRoots::prologue() {
if (ShenandoahStringDedup::is_enabled()) {
StringDedupTable_lock->lock_without_safepoint_check();
StringDedupQueue_lock->lock_without_safepoint_check();
StringDedup::gc_prologue(true);
}
}
void ShenandoahConcurrentStringDedupRoots::epilogue() {
if (ShenandoahStringDedup::is_enabled()) {
StringDedup::gc_epilogue();
StringDedupQueue_lock->unlock();
StringDedupTable_lock->unlock();
}
}
void ShenandoahConcurrentStringDedupRoots::oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id) {
if (ShenandoahStringDedup::is_enabled()) {
assert_locked_or_safepoint_weak(StringDedupQueue_lock);
assert_locked_or_safepoint_weak(StringDedupTable_lock);
StringDedupUnlinkOrOopsDoClosure sd_cl(is_alive, keep_alive);
{
ShenandoahWorkerTimingsTracker x(_phase, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id);
StringDedupQueue::unlink_or_oops_do(&sd_cl);
}
{
ShenandoahWorkerTimingsTracker x(_phase, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id);
StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id);
}
}
}
ShenandoahCodeCacheRoots::ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase) : _phase(phase) {
nmethod::oops_do_marking_prologue();
}
@ -183,7 +126,6 @@ ShenandoahSTWRootScanner::ShenandoahSTWRootScanner(ShenandoahPhaseTimings::Phase
_code_roots(phase),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()),
_vm_roots(phase),
_dedup_roots(phase),
_unload_classes(ShenandoahHeap::heap()->unload_classes()) {
}
@ -274,7 +216,6 @@ ShenandoahRootUpdater::ShenandoahRootUpdater(uint n_workers, ShenandoahPhaseTimi
_cld_roots(phase, n_workers),
_thread_roots(phase, n_workers > 1),
_weak_roots(phase),
_dedup_roots(phase),
_code_roots(phase) {
}
@ -284,7 +225,6 @@ ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTi
_cld_roots(phase, n_workers),
_thread_roots(phase, n_workers > 1),
_weak_roots(phase),
_dedup_roots(phase),
_code_roots(phase) {
assert(ShenandoahHeap::heap()->is_full_gc_in_progress(), "Full GC only");
}
@ -301,7 +241,6 @@ void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) {
// Process light-weight/limited parallel roots then
_vm_roots.oops_do(oops, worker_id);
_weak_roots.oops_do<OopClosure>(oops, worker_id);
_dedup_roots.oops_do(&always_true, oops, worker_id);
_cld_roots.cld_do(&adjust_cld_closure, worker_id);
// Process heavy-weight/fully parallel roots the last
@ -315,15 +254,9 @@ ShenandoahHeapIterationRootScanner::ShenandoahHeapIterationRootScanner() :
_vm_roots(ShenandoahPhaseTimings::heap_iteration_roots),
_cld_roots(ShenandoahPhaseTimings::heap_iteration_roots, 1),
_weak_roots(ShenandoahPhaseTimings::heap_iteration_roots),
_dedup_roots(ShenandoahPhaseTimings::heap_iteration_roots),
_code_roots(ShenandoahPhaseTimings::heap_iteration_roots) {
_dedup_roots.prologue();
}
ShenandoahHeapIterationRootScanner::~ShenandoahHeapIterationRootScanner() {
_dedup_roots.epilogue();
}
void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) {
assert(Thread::current()->is_VM_thread(), "Only by VM thread");
// Must use _claim_none to avoid interfering with concurrent CLDG iteration
@ -337,7 +270,6 @@ ShenandoahHeapIterationRootScanner::~ShenandoahHeapIterationRootScanner() {
// Process light-weight/limited parallel roots then
_vm_roots.oops_do(oops, 0);
_weak_roots.oops_do<OopClosure>(oops, 0);
_dedup_roots.oops_do(&always_true, oops, 0);
_cld_roots.cld_do(&clds, 0);
// Process heavy-weight/fully parallel roots the last

View File

@ -96,29 +96,6 @@ public:
void threads_do(ThreadClosure* tc, uint worker_id);
};
class ShenandoahStringDedupRoots {
private:
ShenandoahPhaseTimings::Phase _phase;
public:
ShenandoahStringDedupRoots(ShenandoahPhaseTimings::Phase phase);
~ShenandoahStringDedupRoots();
void oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id);
};
class ShenandoahConcurrentStringDedupRoots {
private:
ShenandoahPhaseTimings::Phase _phase;
public:
ShenandoahConcurrentStringDedupRoots(ShenandoahPhaseTimings::Phase phase);
void prologue();
void epilogue();
void oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id);
};
class ShenandoahCodeCacheRoots {
private:
ShenandoahPhaseTimings::Phase _phase;
@ -191,7 +168,6 @@ private:
_cld_roots;
ShenandoahVMRoots<false /*concurrent*/>
_vm_roots;
ShenandoahStringDedupRoots _dedup_roots;
const bool _unload_classes;
public:
ShenandoahSTWRootScanner(ShenandoahPhaseTimings::Phase phase);
@ -228,12 +204,10 @@ private:
ShenandoahClassLoaderDataRoots<false /*concurrent*/, true /*single threaded*/>
_cld_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahConcurrentStringDedupRoots _dedup_roots;
ShenandoahCodeCacheRoots _code_roots;
public:
ShenandoahHeapIterationRootScanner();
~ShenandoahHeapIterationRootScanner();
void roots_do(OopClosure* cl);
};
@ -246,7 +220,6 @@ private:
_cld_roots;
ShenandoahThreadRoots _thread_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahStringDedupRoots _dedup_roots;
ShenandoahCodeCacheRoots _code_roots;
public:
@ -264,7 +237,6 @@ private:
_cld_roots;
ShenandoahThreadRoots _thread_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahStringDedupRoots _dedup_roots;
ShenandoahCodeCacheRoots _code_roots;
public:

View File

@ -162,11 +162,9 @@ void ShenandoahSTWRootScanner::roots_do(T* oops, uint worker_id) {
_thread_roots.oops_do(oops, &blobs_cl, worker_id);
_cld_roots.always_strong_cld_do(&clds, worker_id);
} else {
AlwaysTrueClosure always_true;
_thread_roots.oops_do(oops, NULL, worker_id);
_code_roots.code_blobs_do(&blobs_cl, worker_id);
_cld_roots.cld_do(&clds, worker_id);
_dedup_roots.oops_do(&always_true, oops, worker_id);
}
_vm_roots.oops_do<T>(oops, worker_id);
@ -185,7 +183,6 @@ void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAliv
// Process light-weight/limited parallel roots then
_vm_roots.oops_do(keep_alive, worker_id);
_weak_roots.weak_oops_do<IsAlive, KeepAlive>(is_alive, keep_alive, worker_id);
_dedup_roots.oops_do(is_alive, keep_alive, worker_id);
_cld_roots.cld_do(&clds, worker_id);
// Process heavy-weight/fully parallel roots the last

View File

@ -62,10 +62,6 @@ void ShenandoahRootVerifier::roots_do(OopClosure* oops) {
CLDToOopClosure clds(oops, ClassLoaderData::_claim_none);
ClassLoaderDataGraph::cld_do(&clds);
if (ShenandoahStringDedup::is_enabled()) {
ShenandoahStringDedup::oops_do_slow(oops);
}
for (auto id : EnumRange<OopStorageSet::StrongId>()) {
OopStorageSet::storage(id)->oops_do(oops);
}
@ -83,10 +79,6 @@ void ShenandoahRootVerifier::strong_roots_do(OopClosure* oops) {
CLDToOopClosure clds(oops, ClassLoaderData::_claim_none);
ClassLoaderDataGraph::always_strong_cld_do(&clds);
if (ShenandoahStringDedup::is_enabled()) {
ShenandoahStringDedup::oops_do_slow(oops);
}
for (auto id : EnumRange<OopStorageSet::StrongId>()) {
OopStorageSet::storage(id)->oops_do(oops);
}

View File

@ -57,7 +57,7 @@ ShenandoahInitMarkRootsClosure::ShenandoahInitMarkRootsClosure(ShenandoahObjToSc
template <class T>
void ShenandoahInitMarkRootsClosure::do_oop_work(T* p) {
ShenandoahMark::mark_through_ref<T, NO_DEDUP>(p, _queue, _mark_context, false);
ShenandoahMark::mark_through_ref<T, NO_DEDUP>(p, _queue, _mark_context, NULL, false);
}
class ShenandoahSTWMarkTask : public AbstractGangTask {

View File

@ -1,249 +0,0 @@
/*
* Copyright (c) 2017, 2019, Red Hat, Inc. 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/stringDedupThread.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp"
#include "gc/shenandoah/shenandoahStringDedup.inline.hpp"
#include "logging/log.hpp"
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
ShenandoahStrDedupQueue::ShenandoahStrDedupQueue() :
_consumer_queue(NULL),
_num_producer_queue(ShenandoahHeap::heap()->max_workers()),
_published_queues(NULL),
_free_list(NULL),
_num_free_buffer(0),
_max_free_buffer(ShenandoahHeap::heap()->max_workers() * 2),
_cancel(false),
_total_buffers(0) {
_producer_queues = NEW_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _num_producer_queue, mtGC);
for (size_t index = 0; index < _num_producer_queue; index ++) {
_producer_queues[index] = NULL;
}
}
ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
for (size_t index = 0; index < num_queues_nv(); index ++) {
release_buffers(queue_at(index));
}
release_buffers(_free_list);
FREE_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _producer_queues);
}
void ShenandoahStrDedupQueue::wait_impl() {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
while (_consumer_queue == NULL && !_cancel) {
ml.wait();
assert(_consumer_queue == NULL, "Why wait?");
_consumer_queue = _published_queues;
_published_queues = NULL;
}
}
void ShenandoahStrDedupQueue::cancel_wait_impl() {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
_cancel = true;
ml.notify();
}
void ShenandoahStrDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
ShenandoahQueueBuffer* q = queue_at(queue);
while (q != NULL) {
q->unlink_or_oops_do(cl);
q = q->next();
}
}
ShenandoahQueueBuffer* ShenandoahStrDedupQueue::queue_at(size_t queue_id) const {
assert(queue_id <= num_queues(), "Invalid queue id");
if (queue_id < _num_producer_queue) {
return _producer_queues[queue_id];
} else if (queue_id == _num_producer_queue) {
return _consumer_queue;
} else {
assert(queue_id == _num_producer_queue + 1, "Must be");
return _published_queues;
}
}
void ShenandoahStrDedupQueue::set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id) {
assert(queue_id < _num_producer_queue, "Not a producer queue id");
_producer_queues[queue_id] = buf;
}
void ShenandoahStrDedupQueue::push_impl(uint worker_id, oop string_oop) {
assert(worker_id < _num_producer_queue, "Invalid queue id. Can only push to producer queue");
assert(ShenandoahStringDedup::is_candidate(string_oop), "Not a candidate");
ShenandoahQueueBuffer* buf = queue_at((size_t)worker_id);
if (buf == NULL) {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
buf = new_buffer();
set_producer_buffer(buf, worker_id);
} else if (buf->is_full()) {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
buf->set_next(_published_queues);
_published_queues = buf;
buf = new_buffer();
set_producer_buffer(buf, worker_id);
ml.notify();
}
assert(!buf->is_full(), "Sanity");
buf->push(string_oop);
}
oop ShenandoahStrDedupQueue::pop_impl() {
assert(Thread::current() == StringDedupThread::thread(), "Must be dedup thread");
while (true) {
if (_consumer_queue == NULL) {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
_consumer_queue = _published_queues;
_published_queues = NULL;
}
// there is nothing
if (_consumer_queue == NULL) {
return NULL;
}
oop obj = NULL;
if (pop_candidate(obj)) {
assert(ShenandoahStringDedup::is_candidate(obj), "Must be a candidate");
return obj;
}
assert(obj == NULL, "No more candidate");
}
}
bool ShenandoahStrDedupQueue::pop_candidate(oop& obj) {
ShenandoahQueueBuffer* to_release = NULL;
bool suc = true;
do {
if (_consumer_queue->is_empty()) {
ShenandoahQueueBuffer* buf = _consumer_queue;
_consumer_queue = _consumer_queue->next();
buf->set_next(to_release);
to_release = buf;
if (_consumer_queue == NULL) {
suc = false;
break;
}
}
obj = _consumer_queue->pop();
} while (obj == NULL);
if (to_release != NULL) {
MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
release_buffers(to_release);
}
return suc;
}
ShenandoahQueueBuffer* ShenandoahStrDedupQueue::new_buffer() {
assert_lock_strong(StringDedupQueue_lock);
if (_free_list != NULL) {
assert(_num_free_buffer > 0, "Sanity");
ShenandoahQueueBuffer* buf = _free_list;
_free_list = _free_list->next();
_num_free_buffer --;
buf->reset();
return buf;
} else {
assert(_num_free_buffer == 0, "Sanity");
_total_buffers ++;
return new ShenandoahQueueBuffer;
}
}
void ShenandoahStrDedupQueue::release_buffers(ShenandoahQueueBuffer* list) {
assert_lock_strong(StringDedupQueue_lock);
while (list != NULL) {
ShenandoahQueueBuffer* tmp = list;
list = list->next();
if (_num_free_buffer < _max_free_buffer) {
tmp->set_next(_free_list);
_free_list = tmp;
_num_free_buffer ++;
} else {
_total_buffers --;
delete tmp;
}
}
}
void ShenandoahStrDedupQueue::print_statistics_impl() {
Log(gc, stringdedup) log;
log.debug(" Queue:");
log.debug(" Total buffers: " SIZE_FORMAT " (" SIZE_FORMAT " %s). " SIZE_FORMAT " buffers are on free list",
_total_buffers,
byte_size_in_proper_unit(_total_buffers * sizeof(ShenandoahQueueBuffer)),
proper_unit_for_byte_size(_total_buffers * sizeof(ShenandoahQueueBuffer)),
_num_free_buffer);
}
class VerifyQueueClosure : public OopClosure {
private:
ShenandoahHeap* _heap;
public:
VerifyQueueClosure();
void do_oop(oop* o);
void do_oop(narrowOop* o) {
ShouldNotCallThis();
}
};
VerifyQueueClosure::VerifyQueueClosure() :
_heap(ShenandoahHeap::heap()) {
}
void VerifyQueueClosure::do_oop(oop* o) {
if (*o != NULL) {
oop obj = *o;
shenandoah_assert_correct(o, obj);
assert(java_lang_String::is_instance(obj), "Object must be a String");
}
}
void ShenandoahStrDedupQueue::verify_impl() {
VerifyQueueClosure vcl;
for (size_t index = 0; index < num_queues(); index ++) {
ShenandoahQueueBuffer* buf = queue_at(index);
while (buf != NULL) {
buf->oops_do(&vcl);
buf = buf->next();
}
}
}

View File

@ -1,118 +0,0 @@
/*
* Copyright (c) 2017, 2019, Red Hat, Inc. 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_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shenandoah/shenandoahHeap.hpp"
#include "oops/oop.hpp"
template <uint buffer_size>
class ShenandoahOopBuffer : public CHeapObj<mtGC> {
private:
oop _buf[buffer_size];
volatile uint _index;
ShenandoahOopBuffer<buffer_size>* _next;
public:
ShenandoahOopBuffer();
bool is_full() const;
bool is_empty() const;
uint size() const;
void push(oop obj);
oop pop();
void reset();
void set_next(ShenandoahOopBuffer<buffer_size>* next);
ShenandoahOopBuffer<buffer_size>* next() const;
void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl);
void oops_do(OopClosure* cl);
private:
uint index_acquire() const;
void set_index_release(uint index);
};
typedef ShenandoahOopBuffer<64> ShenandoahQueueBuffer;
// Muti-producer and single consumer queue set
class ShenandoahStrDedupQueue : public StringDedupQueue {
private:
ShenandoahQueueBuffer** _producer_queues;
ShenandoahQueueBuffer* _consumer_queue;
size_t _num_producer_queue;
// The queue is used for producers to publish completed buffers
ShenandoahQueueBuffer* _published_queues;
// Cached free buffers
ShenandoahQueueBuffer* _free_list;
size_t _num_free_buffer;
const size_t _max_free_buffer;
bool _cancel;
// statistics
size_t _total_buffers;
private:
~ShenandoahStrDedupQueue();
public:
ShenandoahStrDedupQueue();
void wait_impl();
void cancel_wait_impl();
void push_impl(uint worker_id, oop string_oop);
oop pop_impl();
void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
void print_statistics_impl();
void verify_impl();
protected:
size_t num_queues() const { return num_queues_nv(); }
private:
inline size_t num_queues_nv() const { return (_num_producer_queue + 2); }
ShenandoahQueueBuffer* new_buffer();
void release_buffers(ShenandoahQueueBuffer* list);
ShenandoahQueueBuffer* queue_at(size_t queue_id) const;
bool pop_candidate(oop& obj);
void set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id);
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_HPP

View File

@ -1,126 +0,0 @@
/*
* Copyright (c) 2017, 2020, Red Hat, Inc. 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_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_INLINE_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_INLINE_HPP
#include "gc/shenandoah/shenandoahStrDedupQueue.hpp"
#include "oops/access.hpp"
#include "runtime/atomic.hpp"
// With concurrent string dedup cleaning up, GC worker threads
// may see oops just enqueued, so release_store and load_acquire
// relationship needs to be established between enqueuing threads
// and GC workers.
// For example, when GC sees a slot (index), there must be a valid
// (dead or live) oop.
// Note: There is no concern if GC misses newly enqueued oops,
// since LRB ensures they are in to-space.
template <uint buffer_size>
ShenandoahOopBuffer<buffer_size>::ShenandoahOopBuffer() :
_index(0), _next(NULL) {
}
template <uint buffer_size>
bool ShenandoahOopBuffer<buffer_size>::is_full() const {
return index_acquire() >= buffer_size;
}
template <uint buffer_size>
bool ShenandoahOopBuffer<buffer_size>::is_empty() const {
return index_acquire() == 0;
}
template <uint buffer_size>
uint ShenandoahOopBuffer<buffer_size>::size() const {
return index_acquire();
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::push(oop obj) {
assert(!is_full(), "Buffer is full");
uint idx = index_acquire();
RawAccess<IS_NOT_NULL>::oop_store(&_buf[idx], obj);
set_index_release(idx + 1);
}
template <uint buffer_size>
oop ShenandoahOopBuffer<buffer_size>::pop() {
assert(!is_empty(), "Buffer is empty");
uint idx = index_acquire() - 1;
oop value = NativeAccess<ON_PHANTOM_OOP_REF | AS_NO_KEEPALIVE | MO_ACQUIRE>::oop_load(&_buf[idx]);
set_index_release(idx);
return value;
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::set_next(ShenandoahOopBuffer<buffer_size>* next) {
_next = next;
}
template <uint buffer_size>
ShenandoahOopBuffer<buffer_size>* ShenandoahOopBuffer<buffer_size>::next() const {
return _next;
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::reset() {
_index = 0;
_next = NULL;
}
template <uint buffer_size>
uint ShenandoahOopBuffer<buffer_size>::index_acquire() const {
return Atomic::load_acquire(&_index);
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::set_index_release(uint index) {
return Atomic::release_store(&_index, index);
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl) {
uint len = size();
for (uint index = 0; index < len; index ++) {
oop* obj_addr = &_buf[index];
if (*obj_addr != NULL) {
if (cl->is_alive(*obj_addr)) {
cl->keep_alive(obj_addr);
} else {
RawAccess<MO_RELEASE>::oop_store(&_buf[index], oop());
}
}
}
}
template <uint buffer_size>
void ShenandoahOopBuffer<buffer_size>::oops_do(OopClosure* cl) {
uint len = size();
for (uint index = 0; index < len; index ++) {
cl->do_oop(&_buf[index]);
}
}
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRDEDUPQUEUE_INLINE_HPP

View File

@ -1,136 +0,0 @@
/*
* Copyright (c) 2017, 2020, Red Hat, Inc. 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.inline.hpp"
#include "gc/shared/workgroup.hpp"
#include "gc/shenandoah/shenandoahCollectionSet.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahStringDedup.hpp"
#include "gc/shenandoah/shenandoahStrDedupQueue.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "runtime/thread.hpp"
void ShenandoahStringDedup::initialize() {
assert(UseShenandoahGC, "String deduplication available with Shenandoah GC");
StringDedup::initialize_impl<ShenandoahStrDedupQueue, StringDedupStat>();
}
/* Enqueue candidates for deduplication.
* The method should only be called by GC worker threads during marking phases.
*/
void ShenandoahStringDedup::enqueue_candidate(oop java_string) {
assert(Thread::current()->is_Worker_thread(),
"Only from a GC worker thread");
if (java_string->age() <= StringDeduplicationAgeThreshold) {
const markWord mark = java_string->mark();
// Having/had displaced header, too risk to deal with them, skip
if (mark == markWord::INFLATING() || mark.has_displaced_mark_helper()) {
return;
}
// Increase string age and enqueue it when it rearches age threshold
markWord new_mark = mark.incr_age();
if (mark == java_string->cas_set_mark(new_mark, mark)) {
if (mark.age() == StringDeduplicationAgeThreshold) {
StringDedupQueue::push(ShenandoahWorkerSession::worker_id(), java_string);
}
}
}
}
// Deduplicate a string, return true if it is deduplicated.
void ShenandoahStringDedup::deduplicate(oop java_string) {
assert(is_enabled(), "String deduplication not enabled");
StringDedupStat dummy; // Statistics from this path is never used
StringDedupTable::deduplicate(java_string, &dummy);
}
void ShenandoahStringDedup::parallel_oops_do(ShenandoahPhaseTimings::Phase phase,
BoolObjectClosure* is_alive, OopClosure* cl, uint worker_id) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
assert(is_enabled(), "String deduplication not enabled");
StringDedupUnlinkOrOopsDoClosure sd_cl(is_alive, cl);
{
ShenandoahWorkerTimingsTracker x(phase, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id);
StringDedupQueue::unlink_or_oops_do(&sd_cl);
}
{
ShenandoahWorkerTimingsTracker x(phase, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id);
StringDedupTable::unlink_or_oops_do(&sd_cl, worker_id);
}
}
void ShenandoahStringDedup::oops_do_slow(OopClosure* cl) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
assert(is_enabled(), "String deduplication not enabled");
AlwaysTrueClosure always_true;
StringDedupUnlinkOrOopsDoClosure sd_cl(&always_true, cl);
StringDedupQueue::unlink_or_oops_do(&sd_cl);
StringDedupTable::unlink_or_oops_do(&sd_cl, 0);
}
//
// Task for parallel unlink_or_oops_do() operation on the deduplication queue
// and table.
//
class ShenandoahStringDedupUnlinkOrOopsDoTask : public AbstractGangTask {
private:
StringDedupUnlinkOrOopsDoClosure _cl;
public:
ShenandoahStringDedupUnlinkOrOopsDoTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash) :
AbstractGangTask("Shenandoah String Dedup Unlink/Process"),
_cl(is_alive, keep_alive) {
StringDedup::gc_prologue(allow_resize_and_rehash);
}
~ShenandoahStringDedupUnlinkOrOopsDoTask() {
StringDedup::gc_epilogue();
}
virtual void work(uint worker_id) {
StringDedupQueue::unlink_or_oops_do(&_cl);
StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
}
};
void ShenandoahStringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash) {
assert(is_enabled(), "String deduplication not enabled");
ShenandoahStringDedupUnlinkOrOopsDoTask task(is_alive, keep_alive, allow_resize_and_rehash);
ShenandoahHeap* heap = ShenandoahHeap::heap();
heap->workers()->run_task(&task);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
* Copyright (c) 2017, 2021, Red Hat, Inc. 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
@ -26,33 +26,10 @@
#define SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_HPP
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "memory/iterator.hpp"
class ShenandoahStringDedup : public StringDedup {
public:
// Initialize string deduplication.
static void initialize();
// Enqueue a string to worker's local string dedup queue
static void enqueue_candidate(oop java_string);
// Deduplicate a string, the call is lock-free
static void deduplicate(oop java_string);
static void parallel_oops_do(ShenandoahPhaseTimings::Phase phase,
BoolObjectClosure* is_alive,
OopClosure* cl,
uint worker_id);
static void oops_do_slow(OopClosure* cl);
static inline bool is_candidate(oop obj);
static void unlink_or_oops_do(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool allow_resize_and_rehash);
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Red Hat, Inc. All rights reserved.
* Copyright (c) 2019, 2021, Red Hat, Inc. 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
@ -29,8 +29,26 @@
#include "gc/shenandoah/shenandoahStringDedup.hpp"
bool ShenandoahStringDedup::is_candidate(oop obj) {
return java_lang_String::is_instance_inlined(obj) &&
java_lang_String::value(obj) != NULL;
assert(Thread::current()->is_Worker_thread(),
"Only from a GC worker thread");
if (!java_lang_String::is_instance_inlined(obj) ||
java_lang_String::value(obj) == nullptr) {
return false;
}
if (StringDedup::is_below_threshold_age(obj->age())) {
const markWord mark = obj->mark();
// Having/had displaced header, too risk to deal with them, skip
if (mark == markWord::INFLATING() || mark.has_displaced_mark_helper()) {
return false;
}
// Increase string age and enqueue it when it rearches age threshold
markWord new_mark = mark.incr_age();
if (mark == obj->cas_set_mark(new_mark, mark)) {
return StringDedup::is_threshold_age(new_mark.age());
}
}
return false;
}
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP

View File

@ -143,6 +143,7 @@ class AllocatedObj {
f(mtSynchronizer, "Synchronization") \
f(mtServiceability, "Serviceability") \
f(mtMetaspace, "Metaspace") \
f(mtStringDedup, "String Deduplication") \
f(mtNone, "Unknown") \
//end

View File

@ -42,6 +42,7 @@
#include "gc/shared/gcLogPrecious.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
@ -1066,6 +1067,8 @@ void Universe::initialize_verify_flags() {
verify_flags |= Verify_CodeCacheOops;
} else if (strcmp(token, "resolved_method_table") == 0) {
verify_flags |= Verify_ResolvedMethodTable;
} else if (strcmp(token, "stringdedup") == 0) {
verify_flags |= Verify_StringDedup;
} else {
vm_exit_during_initialization(err_msg("VerifySubSet: \'%s\' memory sub-system is unknown, please correct it", token));
}
@ -1118,12 +1121,10 @@ void Universe::verify(VerifyOption option, const char* prefix) {
StringTable::verify();
}
if (should_verify_subset(Verify_CodeCache)) {
{
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
log_debug(gc, verify)("CodeCache");
CodeCache::verify();
}
}
if (should_verify_subset(Verify_SystemDictionary)) {
log_debug(gc, verify)("SystemDictionary");
SystemDictionary::verify();
@ -1148,6 +1149,10 @@ void Universe::verify(VerifyOption option, const char* prefix) {
log_debug(gc, verify)("ResolvedMethodTable Oops");
ResolvedMethodTable::verify();
}
if (should_verify_subset(Verify_StringDedup)) {
log_debug(gc, verify)("String Deduplication");
StringDedup::verify();
}
_verify_in_progress = false;
}

View File

@ -343,6 +343,7 @@ class Universe: AllStatic {
Verify_JNIHandles = 256,
Verify_CodeCacheOops = 512,
Verify_ResolvedMethodTable = 1024,
Verify_StringDedup = 2048,
Verify_All = -1
};
static void initialize_verify_flags();

View File

@ -33,6 +33,7 @@
#include "compiler/compilerDefinitions.hpp"
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/gcConfig.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "logging/log.hpp"
#include "logging/logConfiguration.hpp"
@ -3998,6 +3999,10 @@ jint Arguments::apply_ergo() {
// Initialize Metaspace flags and alignments
Metaspace::ergo_initialize();
if (!StringDedup::ergo_initialize()) {
return JNI_EINVAL;
}
// Set compiler flags after GC is selected and GC specific
// flags (LoopStripMiningIter) are set.
CompilerConfig::ergo_initialize();

View File

@ -1940,16 +1940,39 @@ const intx ObjectAlignmentInBytes = 8;
product(bool, UseStringDeduplication, false, \
"Use string deduplication") \
\
product(uintx, StringDeduplicationAgeThreshold, 3, \
product(uint, StringDeduplicationAgeThreshold, 3, \
"A string must reach this age (or be promoted to an old region) " \
"to be considered for deduplication") \
range(1, markWord::max_age) \
\
product(bool, StringDeduplicationResizeALot, false, DIAGNOSTIC, \
"Force table resize every time the table is scanned") \
product(size_t, StringDeduplicationInitialTableSize, 500, EXPERIMENTAL, \
"Approximate initial number of buckets in the table") \
range(1, 1 * G) \
\
product(bool, StringDeduplicationRehashALot, false, DIAGNOSTIC, \
"Force table rehash every time the table is scanned") \
product(double, StringDeduplicationGrowTableLoad, 14.0, EXPERIMENTAL, \
"Entries per bucket above which the table should be expanded") \
range(0.1, 1000.0) \
\
product(double, StringDeduplicationShrinkTableLoad, 1.0, EXPERIMENTAL, \
"Entries per bucket below which the table should be shrunk") \
range(0.01, 100.0) \
\
product(double, StringDeduplicationTargetTableLoad, 7.0, EXPERIMENTAL, \
"Desired entries per bucket when resizing the table") \
range(0.01, 1000.0) \
\
product(size_t, StringDeduplicationCleanupDeadMinimum, 100, EXPERIMENTAL, \
"Minimum number of dead table entries for cleaning the table") \
\
product(int, StringDeduplicationCleanupDeadPercent, 5, EXPERIMENTAL, \
"Minimum percentage of dead table entries for cleaning the table") \
range(1, 100) \
\
product(bool, StringDeduplicationResizeALot, false, DIAGNOSTIC, \
"Force more frequent table resizing") \
\
product(uint64_t, StringDeduplicationHashSeed, 0, DIAGNOSTIC, \
"Seed for the table hashing function; 0 requests computed seed") \
\
product(bool, WhiteBoxAPI, false, DIAGNOSTIC, \
"Enable internal testing APIs") \

View File

@ -34,6 +34,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compilerOracle.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "interpreter/bytecodeHistogram.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/support/jfrThreadId.hpp"
@ -462,6 +463,11 @@ void before_exit(JavaThread* thread) {
StatSampler::disengage();
StatSampler::destroy();
// Shut down string deduplication if running.
if (StringDedup::is_enabled()) {
StringDedup::stop();
}
// Stop concurrent GC threads
Universe::heap()->stop();

View File

@ -59,8 +59,8 @@ Mutex* AdapterHandlerLibrary_lock = NULL;
Mutex* SignatureHandlerLibrary_lock = NULL;
Mutex* VtableStubs_lock = NULL;
Mutex* SymbolArena_lock = NULL;
Monitor* StringDedupQueue_lock = NULL;
Mutex* StringDedupTable_lock = NULL;
Monitor* StringDedup_lock = NULL;
Mutex* StringDedupIntern_lock = NULL;
Monitor* CodeCache_lock = NULL;
Monitor* CodeSweeper_lock = NULL;
Mutex* MethodData_lock = NULL;
@ -223,18 +223,13 @@ void mutex_init() {
def(Uncommit_lock , PaddedMutex , leaf + 1 , true, _safepoint_check_never);
def(RootRegionScan_lock , PaddedMonitor, leaf , true, _safepoint_check_never);
def(StringDedupQueue_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
def(StringDedupTable_lock , PaddedMutex , leaf, true, _safepoint_check_never);
def(MarkStackFreeList_lock , PaddedMutex , leaf , true, _safepoint_check_never);
def(MarkStackChunkList_lock , PaddedMutex , leaf , true, _safepoint_check_never);
def(MonitoringSupport_lock , PaddedMutex , native , true, _safepoint_check_never); // used for serviceability monitoring support
}
if (UseShenandoahGC) {
def(StringDedupQueue_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
def(StringDedupTable_lock , PaddedMutex , leaf + 1, true, _safepoint_check_never);
}
def(StringDedup_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
def(StringDedupIntern_lock , PaddedMutex , leaf, true, _safepoint_check_never);
def(ParGCRareEvent_lock , PaddedMutex , leaf, true, _safepoint_check_always);
def(CodeCache_lock , PaddedMonitor, special, true, _safepoint_check_never);
def(CodeSweeper_lock , PaddedMonitor, special-2, true, _safepoint_check_never);

View File

@ -51,8 +51,8 @@ extern Mutex* AdapterHandlerLibrary_lock; // a lock on the AdapterHandler
extern Mutex* SignatureHandlerLibrary_lock; // a lock on the SignatureHandlerLibrary
extern Mutex* VtableStubs_lock; // a lock on the VtableStubs
extern Mutex* SymbolArena_lock; // a lock on the symbol table arena
extern Monitor* StringDedupQueue_lock; // a lock on the string deduplication queue
extern Mutex* StringDedupTable_lock; // a lock on the string deduplication table
extern Monitor* StringDedup_lock; // a lock on the string deduplication facility
extern Mutex* StringDedupIntern_lock; // a lock on StringTable notification of StringDedup
extern Monitor* CodeCache_lock; // a lock on the CodeCache, rank is special
extern Monitor* CodeSweeper_lock; // a lock used by the sweeper only for wait notify
extern Mutex* MethodData_lock; // a lock on installation of method data

View File

@ -44,6 +44,7 @@
#include "gc/shared/gcVMOperations.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "interpreter/interpreter.hpp"
#include "interpreter/linkResolver.hpp"
@ -3756,6 +3757,9 @@ 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());
st->flush();
@ -3816,6 +3820,11 @@ 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));

View File

@ -198,13 +198,8 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outp
}
#ifndef PRODUCT
template <class T> void print_literal(T l) {
l->print();
}
static void print_literal(WeakHandle l) {
l.print();
}
template <class T> static void print_literal(T const& l) { l.print(); }
template <class T> static void print_literal(T* l) { print_literal(*l); }
template <class T, MEMFLAGS F> void Hashtable<T, F>::print() {
ResourceMark rm;

View File

@ -131,9 +131,6 @@ public class TestGCLogMessages {
new LogMessageWithLevel("Redirtied Cards", Level.DEBUG),
// Misc Top-level
new LogMessageWithLevel("Purge Code Roots", Level.DEBUG),
new LogMessageWithLevel("String Deduplication", Level.DEBUG),
new LogMessageWithLevel("Queue Fixup", Level.DEBUG),
new LogMessageWithLevel("Table Fixup", Level.DEBUG),
new LogMessageWithLevel("Expand Heap After Collection", Level.DEBUG),
new LogMessageWithLevel("Region Register", Level.DEBUG),
new LogMessageWithLevel("Prepare Heap Roots", Level.DEBUG),
@ -197,7 +194,6 @@ public class TestGCLogMessages {
output.shouldHaveExitValue(0);
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
"-XX:+UseStringDeduplication",
"-Xmx10M",
"-Xlog:gc+phases=debug",
GCTest.class.getName());
@ -206,7 +202,6 @@ public class TestGCLogMessages {
checkMessagesAtLevel(output, allLogMessages, Level.DEBUG);
pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
"-XX:+UseStringDeduplication",
"-Xmx10M",
"-Xlog:gc+phases=trace",
GCTest.class.getName());

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2014, 2020, 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 gc.g1;
/*
* @test TestStringDeduplicationTableRehash
* @summary Test string deduplication table rehash
* @bug 8029075
* @requires vm.gc.G1
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationTableRehash
*/
public class TestStringDeduplicationTableRehash {
public static void main(String[] args) throws Exception {
TestStringDeduplicationTools.testTableRehash();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationAgeThreshold
* @summary Test string deduplication age threshold
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationAgeThreshold
* @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold
*/
public class TestStringDeduplicationAgeThreshold {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationFullGC
* @summary Test string deduplication during full GC
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationFullGC
* @run driver gc.stringdedup.TestStringDeduplicationFullGC
*/
public class TestStringDeduplicationFullGC {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationInterned
* @summary Test string deduplication of interned strings
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationInterned
* @run driver gc.stringdedup.TestStringDeduplicationInterned
*/
public class TestStringDeduplicationInterned {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationPrintOptions
* @summary Test string deduplication print options
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationPrintOptions
* @run driver gc.stringdedup.TestStringDeduplicationPrintOptions
*/
public class TestStringDeduplicationPrintOptions {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationTableResize
* @summary Test string deduplication table resize
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationTableResize
* @run driver gc.stringdedup.TestStringDeduplicationTableResize
*/
public class TestStringDeduplicationTableResize {

View File

@ -21,7 +21,7 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* Common code for string deduplication tests
@ -113,6 +113,27 @@ class TestStringDeduplicationTools {
}
}
private static boolean waitForDeduplication(String s1, String s2) {
boolean first = true;
int timeout = 10000; // 10sec in ms
int iterationWait = 100; // 100ms
for (int attempts = 0; attempts < (timeout / iterationWait); attempts++) {
if (getValue(s1) == getValue(s2)) {
return true;
}
if (first) {
System.out.println("Waiting for deduplication...");
first = false;
}
try {
Thread.sleep(iterationWait);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return false;
}
private static String generateString(int id) {
StringBuilder builder = new StringBuilder(StringLength);
@ -199,7 +220,6 @@ class TestStringDeduplicationTools {
"-Xmn" + Xmn + "m",
"-Xms" + Xms + "m",
"-Xmx" + Xmx + "m",
"-XX:+UseG1GC",
"-XX:+UnlockDiagnosticVMOptions",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"-XX:+VerifyAfterGC" // Always verify after GC
@ -209,7 +229,7 @@ class TestStringDeduplicationTools {
args.addAll(Arrays.asList(defaultArgs));
args.addAll(Arrays.asList(extraArgs));
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
ProcessBuilder pb = ProcessTools.createTestJvm(args);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
System.err.println(output.getStderr());
System.out.println(output.getStdout());
@ -273,19 +293,7 @@ class TestStringDeduplicationTools {
// and be inserted into the deduplication hashtable.
forceDeduplication(ageThreshold, FullGC);
// Wait for deduplication to occur
for (int attempts = 0; attempts < 10; attempts++) {
if (getValue(dupString1) == getValue(baseString)) {
break;
}
System.out.println("Waiting...");
try {
Thread.sleep(1000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (getValue(dupString1) != getValue(baseString)) {
if (!waitForDeduplication(dupString1, baseString)) {
throw new RuntimeException("Deduplication has not occurred");
}
@ -299,12 +307,16 @@ class TestStringDeduplicationTools {
// Intern the new duplicate
Object beforeInternedValue = getValue(dupString2);
String internedString = dupString2.intern();
Object afterInternedValue = getValue(dupString2);
// Force internedString to be inspected for deduplication.
// Because it was interned it should be queued up for
// dedup, even though it hasn't reached the age threshold.
doYoungGc(1);
if (internedString != dupString2) {
throw new RuntimeException("String should match");
}
if (getValue(internedString) != getValue(baseString)) {
throw new RuntimeException("Values should match");
}
// Check original value of interned string, to make sure
// deduplication happened on the interned string and not
@ -313,11 +325,30 @@ class TestStringDeduplicationTools {
throw new RuntimeException("Values should not match");
}
// Create duplicate of baseString
StringBuilder sb3 = new StringBuilder(baseString);
String dupString3 = sb3.toString();
if (getValue(dupString3) == getValue(baseString)) {
throw new RuntimeException("Values should not match");
}
forceDeduplication(ageThreshold, FullGC);
if (!waitForDeduplication(dupString3, baseString)) {
if (getValue(dupString3) != getValue(internedString)) {
throw new RuntimeException("String 3 doesn't match either");
}
}
if (afterInternedValue != getValue(dupString2)) {
throw new RuntimeException("Interned string value changed");
}
System.out.println("End: InternedTest");
}
public static OutputAnalyzer run() throws Exception {
return runTest("-Xlog:gc=debug,gc+stringdedup=trace",
return runTest("-Xlog:gc=debug,stringdedup*=debug",
"-XX:+UseStringDeduplication",
"-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold,
InternedTest.class.getName(),
@ -341,9 +372,7 @@ class TestStringDeduplicationTools {
OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
DefaultAgeThreshold,
YoungGC,
"-Xlog:gc,gc+stringdedup=trace");
output.shouldNotContain("Full GC");
output.shouldContain("Pause Young (Normal) (G1 Evacuation Pause)");
"-Xlog:gc*,stringdedup*=debug");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
output.shouldHaveExitValue(0);
@ -354,9 +383,7 @@ class TestStringDeduplicationTools {
OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
DefaultAgeThreshold,
FullGC,
"-Xlog:gc,gc+stringdedup=trace");
output.shouldNotContain("Pause Young (Normal) (G1 Evacuation Pause)");
output.shouldContain("Full GC");
"-Xlog:gc*,stringdedup*=debug");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
output.shouldHaveExitValue(0);
@ -367,7 +394,7 @@ class TestStringDeduplicationTools {
OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
DefaultAgeThreshold,
YoungGC,
"-Xlog:gc,gc+stringdedup=trace",
"-Xlog:gc*,stringdedup*=debug",
"-XX:+StringDeduplicationResizeALot");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
@ -375,24 +402,6 @@ class TestStringDeduplicationTools {
output.shouldHaveExitValue(0);
}
public static void testTableRehash() throws Exception {
// Test with StringDeduplicationRehashALot
OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
DefaultAgeThreshold,
YoungGC,
"-Xlog:gc,gc+stringdedup=trace",
"-XX:+StringDeduplicationRehashALot");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
// Ensure there have been some rehashes. Can't check for never
// being zero, because the first collection might trigger a resize,
// which suppresses rehash. But as written, some collections should
// not lead to a resize, and those will do a rehash.
output.shouldMatch(".* Rehash Count: [1-9].*");
output.shouldNotContain("Hash Seed: 0x0");
output.shouldHaveExitValue(0);
}
public static void testAgeThreshold() throws Exception {
OutputAnalyzer output;
@ -400,7 +409,7 @@ class TestStringDeduplicationTools {
output = DeduplicationTest.run(SmallNumberOfStrings,
MaxAgeThreshold,
YoungGC,
"-Xlog:gc,gc+stringdedup=trace");
"-Xlog:gc*,stringdedup*=debug");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
output.shouldHaveExitValue(0);
@ -409,7 +418,7 @@ class TestStringDeduplicationTools {
output = DeduplicationTest.run(SmallNumberOfStrings,
MinAgeThreshold,
YoungGC,
"-Xlog:gc,gc+stringdedup=trace");
"-Xlog:gc*,stringdedup*=debug");
output.shouldContain("Concurrent String Deduplication");
output.shouldContain("Deduplicated:");
output.shouldHaveExitValue(0);
@ -440,11 +449,11 @@ class TestStringDeduplicationTools {
output.shouldNotContain("Deduplicated:");
output.shouldHaveExitValue(0);
// Test with -Xlog:gc+stringdedup
// Test with -Xlog:stringdedup
output = DeduplicationTest.run(SmallNumberOfStrings,
DefaultAgeThreshold,
YoungGC,
"-Xlog:gc+stringdedup");
"-Xlog:stringdedup");
output.shouldContain("Concurrent String Deduplication");
output.shouldNotContain("Deduplicated:");
output.shouldHaveExitValue(0);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -21,19 +21,19 @@
* questions.
*/
package gc.g1;
package gc.stringdedup;
/*
* @test TestStringDeduplicationYoungGC
* @summary Test string deduplication during young GC
* @bug 8029075
* @requires vm.gc.G1
* @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah"
* @library /test/lib
* @library /
* @modules java.base/jdk.internal.misc:open
* @modules java.base/java.lang:open
* java.management
* @run driver gc.g1.TestStringDeduplicationYoungGC
* @run driver gc.stringdedup.TestStringDeduplicationYoungGC
*/
public class TestStringDeduplicationYoungGC {