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:
parent
360928d16d
commit
be0a655208
@ -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");
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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") \
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
166
src/hotspot/share/gc/shared/stringdedup/stringDedupConfig.cpp
Normal file
166
src/hotspot/share/gc/shared/stringdedup/stringDedupConfig.cpp
Normal 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();
|
||||
}
|
@ -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
|
||||
|
227
src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp
Normal file
227
src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp
Normal 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{};
|
||||
}
|
@ -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
|
@ -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");
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -641,8 +641,6 @@ public:
|
||||
|
||||
void trash_humongous_region_at(ShenandoahHeapRegion *r);
|
||||
|
||||
void deduplicate_string(oop str);
|
||||
|
||||
private:
|
||||
void trash_cset_regions();
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) :
|
||||
MetadataVisitingOopIterateClosure(rp),
|
||||
_stringDedup_requests(),
|
||||
_queue(q),
|
||||
_mark_context(ShenandoahHeap::heap()->marking_context()),
|
||||
_weak(false)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -143,6 +143,7 @@ class AllocatedObj {
|
||||
f(mtSynchronizer, "Synchronization") \
|
||||
f(mtServiceability, "Serviceability") \
|
||||
f(mtMetaspace, "Metaspace") \
|
||||
f(mtStringDedup, "String Deduplication") \
|
||||
f(mtNone, "Unknown") \
|
||||
//end
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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") \
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 {
|
@ -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 {
|
@ -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 {
|
@ -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 {
|
@ -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 {
|
@ -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);
|
@ -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 {
|
Loading…
Reference in New Issue
Block a user