8203641: Refactor String Deduplication into shared
Summary: Allows string deduplication to be shared among different collectors
Reviewed-by: tschatzl, rkennke
--- a/src/hotspot/share/gc/g1/g1StringDedup.cpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedup.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -29,26 +29,16 @@
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1StringDedupQueue.hpp"
#include "gc/g1/g1StringDedupStat.hpp"
-#include "gc/g1/g1StringDedupTable.hpp"
-#include "gc/g1/g1StringDedupThread.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 "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
-bool G1StringDedup::_enabled = false;
-
void G1StringDedup::initialize() {
- assert(UseG1GC, "String deduplication only available with G1");
- if (UseStringDeduplication) {
- _enabled = true;
- G1StringDedupQueue::create();
- G1StringDedupTable::create();
- G1StringDedupThread::create();
- }
-}
-
-void G1StringDedup::stop() {
- assert(is_enabled(), "String deduplication not enabled");
- G1StringDedupThread::thread()->stop();
+ assert(UseG1GC, "String deduplication available with G1");
+ StringDedup::initialize_impl<G1StringDedupQueue, G1StringDedupStat>();
}
bool G1StringDedup::is_candidate_from_mark(oop obj) {
@@ -99,12 +89,6 @@
}
}
-void G1StringDedup::deduplicate(oop java_string) {
- assert(is_enabled(), "String deduplication not enabled");
- G1StringDedupStat dummy; // Statistics from this path is never used
- G1StringDedupTable::deduplicate(java_string, dummy);
-}
-
void G1StringDedup::oops_do(OopClosure* keep_alive) {
assert(is_enabled(), "String deduplication not enabled");
unlink_or_oops_do(NULL, keep_alive, true /* allow_resize_and_rehash */);
@@ -112,8 +96,8 @@
void G1StringDedup::parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) {
assert(is_enabled(), "String deduplication not enabled");
- G1StringDedupQueue::unlink_or_oops_do(unlink);
- G1StringDedupTable::unlink_or_oops_do(unlink, worker_id);
+ StringDedupQueue::unlink_or_oops_do(unlink);
+ StringDedupTable::unlink_or_oops_do(unlink, worker_id);
}
//
@@ -136,11 +120,11 @@
virtual void work(uint worker_id) {
{
G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupQueueFixup, worker_id);
- G1StringDedupQueue::unlink_or_oops_do(&_cl);
+ StringDedupQueue::unlink_or_oops_do(&_cl);
}
{
G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupTableFixup, worker_id);
- G1StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
+ StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
}
}
};
@@ -155,61 +139,3 @@
G1CollectedHeap* g1h = G1CollectedHeap::heap();
g1h->workers()->run_task(&task);
}
-
-void G1StringDedup::threads_do(ThreadClosure* tc) {
- assert(is_enabled(), "String deduplication not enabled");
- tc->do_thread(G1StringDedupThread::thread());
-}
-
-void G1StringDedup::print_worker_threads_on(outputStream* st) {
- assert(is_enabled(), "String deduplication not enabled");
- G1StringDedupThread::thread()->print_on(st);
- st->cr();
-}
-
-void G1StringDedup::verify() {
- assert(is_enabled(), "String deduplication not enabled");
- G1StringDedupQueue::verify();
- G1StringDedupTable::verify();
-}
-
-G1StringDedupUnlinkOrOopsDoClosure::G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
- OopClosure* keep_alive,
- bool allow_resize_and_rehash) :
- _is_alive(is_alive),
- _keep_alive(keep_alive),
- _resized_table(NULL),
- _rehashed_table(NULL),
- _next_queue(0),
- _next_bucket(0) {
- if (allow_resize_and_rehash) {
- // If both resize and rehash is needed, only do resize. Rehash of
- // the table will eventually happen if the situation persists.
- _resized_table = G1StringDedupTable::prepare_resize();
- if (!is_resizing()) {
- _rehashed_table = G1StringDedupTable::prepare_rehash();
- }
- }
-}
-
-G1StringDedupUnlinkOrOopsDoClosure::~G1StringDedupUnlinkOrOopsDoClosure() {
- assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash");
- if (is_resizing()) {
- G1StringDedupTable::finish_resize(_resized_table);
- } else if (is_rehashing()) {
- G1StringDedupTable::finish_rehash(_rehashed_table);
- }
-}
-
-// Atomically claims the next available queue for exclusive access by
-// the current thread. Returns the queue number of the claimed queue.
-size_t G1StringDedupUnlinkOrOopsDoClosure::claim_queue() {
- return Atomic::add((size_t)1, &_next_queue) - 1;
-}
-
-// Atomically claims the next available table partition for exclusive
-// access by the current thread. Returns the table bucket number where
-// the claimed partition starts.
-size_t G1StringDedupUnlinkOrOopsDoClosure::claim_table_partition(size_t partition_size) {
- return Atomic::add(partition_size, &_next_bucket) - partition_size;
-}
--- a/src/hotspot/share/gc/g1/g1StringDedup.hpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedup.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -26,30 +26,7 @@
#define SHARE_VM_GC_G1_G1STRINGDEDUP_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.
-//
-// 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.
-//
-// 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.
-//
-// Candidate selection
+// G1 string deduplication candidate selection
//
// An object is considered a deduplication candidate if all of the following
// statements are true:
@@ -70,36 +47,21 @@
// than the deduplication age threshold, is will never become a candidate again.
// This approach avoids making the same object a candidate more than once.
//
-// 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.
-//
-// For additional information on string deduplication, please see JEP 192,
-// http://openjdk.java.net/jeps/192
-//
+#include "gc/shared/stringdedup/stringDedup.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
class OopClosure;
class BoolObjectClosure;
-class ThreadClosure;
-class outputStream;
-class G1StringDedupTable;
+class G1GCPhaseTimes;
class G1StringDedupUnlinkOrOopsDoClosure;
-class G1GCPhaseTimes;
//
-// Main interface for interacting with string deduplication.
+// G1 interface for interacting with string deduplication.
//
-class G1StringDedup : public AllStatic {
+class G1StringDedup : public StringDedup {
private:
- // Single state for checking if both G1 and string deduplication is enabled.
- static bool _enabled;
// Candidate selection policies, returns true if the given object is
// candidate for string deduplication.
@@ -107,21 +69,9 @@
static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj);
public:
- // Returns true if both G1 and string deduplication is enabled.
- static bool is_enabled() {
- return _enabled;
- }
-
// Initialize string deduplication.
static void initialize();
- // Stop the deduplication thread.
- static void stop();
-
- // Immediately deduplicates the given String object, bypassing the
- // the deduplication queue.
- static void deduplicate(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.
@@ -133,70 +83,28 @@
static void parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id);
static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive,
bool allow_resize_and_rehash, G1GCPhaseTimes* phase_times = NULL);
-
- static void threads_do(ThreadClosure* tc);
- static void print_worker_threads_on(outputStream* st);
- static void verify();
};
//
// This closure encapsulates the state and the closures needed when scanning
// the deduplication queue and table during the unlink_or_oops_do() operation.
// A single instance of this closure is created and then shared by all worker
-// threads participating in the scan. The _next_queue and _next_bucket fields
-// provide a simple mechanism for GC workers to claim exclusive access to a
-// queue or a table partition.
+// threads participating in the scan.
//
-class G1StringDedupUnlinkOrOopsDoClosure : public StackObj {
-private:
- BoolObjectClosure* _is_alive;
- OopClosure* _keep_alive;
- G1StringDedupTable* _resized_table;
- G1StringDedupTable* _rehashed_table;
- size_t _next_queue;
- size_t _next_bucket;
-
+class G1StringDedupUnlinkOrOopsDoClosure : public StringDedupUnlinkOrOopsDoClosure {
public:
G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
- bool allow_resize_and_rehash);
- ~G1StringDedupUnlinkOrOopsDoClosure();
-
- bool is_resizing() {
- return _resized_table != NULL;
- }
-
- G1StringDedupTable* resized_table() {
- return _resized_table;
- }
-
- bool is_rehashing() {
- return _rehashed_table != NULL;
- }
-
- // Atomically claims the next available queue for exclusive access by
- // the current thread. Returns the queue number of the claimed queue.
- size_t claim_queue();
+ bool allow_resize_and_rehash) :
+ StringDedupUnlinkOrOopsDoClosure(is_alive, keep_alive) {
+ if (G1StringDedup::is_enabled()) {
+ G1StringDedup::gc_prologue(allow_resize_and_rehash);
+ }
+ }
- // Atomically claims the next available table partition for exclusive
- // access by the current thread. Returns the table bucket number where
- // the claimed partition starts.
- size_t claim_table_partition(size_t partition_size);
-
- // Applies and returns the result from the is_alive closure, or
- // returns true if no such closure was provided.
- bool is_alive(oop o) {
- if (_is_alive != NULL) {
- return _is_alive->do_object_b(o);
- }
- return true;
- }
-
- // Applies the keep_alive closure, or does nothing if no such
- // closure was provided.
- void keep_alive(oop* p) {
- if (_keep_alive != NULL) {
- _keep_alive->do_oop(p);
+ ~G1StringDedupUnlinkOrOopsDoClosure() {
+ if (G1StringDedup::is_enabled()) {
+ G1StringDedup::gc_epilogue();
}
}
};
--- a/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedupQueue.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -34,7 +34,6 @@
#include "runtime/safepointVerifiers.hpp"
#include "utilities/stack.inline.hpp"
-G1StringDedupQueue* G1StringDedupQueue::_queue = NULL;
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
@@ -54,54 +53,49 @@
ShouldNotReachHere();
}
-void G1StringDedupQueue::create() {
- assert(_queue == NULL, "One string deduplication queue allowed");
- _queue = new G1StringDedupQueue();
-}
-
-void G1StringDedupQueue::wait() {
+void G1StringDedupQueue::wait_impl() {
MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
- while (_queue->_empty && !_queue->_cancel) {
+ while (_empty && !_cancel) {
ml.wait(Mutex::_no_safepoint_check_flag);
}
}
-void G1StringDedupQueue::cancel_wait() {
+void G1StringDedupQueue::cancel_wait_impl() {
MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
- _queue->_cancel = true;
+ _cancel = true;
ml.notify();
}
-void G1StringDedupQueue::push(uint worker_id, oop java_string) {
+void G1StringDedupQueue::push_impl(uint worker_id, oop java_string) {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
- assert(worker_id < _queue->_nqueues, "Invalid queue");
+ assert(worker_id < _nqueues, "Invalid queue");
// Push and notify waiter
- G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id];
+ G1StringDedupWorkerQueue& worker_queue = _queues[worker_id];
if (!worker_queue.is_full()) {
worker_queue.push(java_string);
- if (_queue->_empty) {
+ if (_empty) {
MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
- if (_queue->_empty) {
+ if (_empty) {
// Mark non-empty and notify waiter
- _queue->_empty = false;
+ _empty = false;
ml.notify();
}
}
} else {
// Queue is full, drop the string and update the statistics
- Atomic::inc(&_queue->_dropped);
+ Atomic::inc(&_dropped);
}
}
-oop G1StringDedupQueue::pop() {
+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 < _queue->_nqueues; tries++) {
+ for (size_t tries = 0; tries < _nqueues; tries++) {
// The cursor indicates where we left of last time
- G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor];
+ G1StringDedupWorkerQueue* queue = &_queues[_cursor];
while (!queue->is_empty()) {
oop obj = queue->pop();
// The oop we pop can be NULL if it was marked
@@ -112,34 +106,18 @@
}
// Try next queue
- _queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues;
+ _cursor = (_cursor + 1) % _nqueues;
}
// Mark empty
- _queue->_empty = true;
+ _empty = true;
return NULL;
}
-void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) {
- // A worker thread first claims a queue, which ensures exclusive
- // access to that queue, then continues to process it.
- for (;;) {
- // Grab next queue to scan
- size_t queue = cl->claim_queue();
- if (queue >= _queue->_nqueues) {
- // End of queues
- break;
- }
-
- // Scan the queue
- unlink_or_oops_do(cl, queue);
- }
-}
-
-void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
- assert(queue < _queue->_nqueues, "Invalid queue");
- StackIterator<oop, mtGC> iter(_queue->_queues[queue]);
+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) {
@@ -153,14 +131,14 @@
}
}
-void G1StringDedupQueue::print_statistics() {
+void G1StringDedupQueue::print_statistics_impl() {
log_debug(gc, stringdedup)(" Queue");
- log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _queue->_dropped);
+ log_debug(gc, stringdedup)(" Dropped: " UINTX_FORMAT, _dropped);
}
-void G1StringDedupQueue::verify() {
- for (size_t i = 0; i < _queue->_nqueues; i++) {
- StackIterator<oop, mtGC> iter(_queue->_queues[i]);
+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) {
--- a/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedupQueue.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,40 +25,21 @@
#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP
#define SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP
+#include "gc/shared/stringdedup/stringDedupQueue.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
#include "utilities/stack.hpp"
-class G1StringDedupUnlinkOrOopsDoClosure;
+class StringDedupUnlinkOrOopsDoClosure;
//
-// The deduplication queue acts as the communication channel between the stop-the-world
-// 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.
+// G1 enqueues candidates during the stop-the-world mark/evacuation phase.
//
-// 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), but only allowed during a safepoint. Popping from the queue is NOT thread safe
-// and can only be done by the deduplication thread outside a safepoint.
-//
-// The StringDedupQueue_lock is only used for blocking and waking up the deduplication
-// thread in case the queue is empty or becomes non-empty, respectively. This lock does
-// not otherwise protect the queue content.
-//
-class G1StringDedupQueue : public CHeapObj<mtGC> {
+
+class G1StringDedupQueue : public StringDedupQueue {
private:
typedef Stack<oop, mtGC> G1StringDedupWorkerQueue;
- static G1StringDedupQueue* _queue;
static const size_t _max_size;
static const size_t _max_cache_size;
@@ -71,31 +52,36 @@
// Statistics counter, only used for logging.
uintx _dropped;
- G1StringDedupQueue();
~G1StringDedupQueue();
- static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
+ void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
public:
- static void create();
+ G1StringDedupQueue();
+
+protected:
// Blocks and waits for the queue to become non-empty.
- static void wait();
+ void wait_impl();
// Wakes up any thread blocked waiting for the queue to become non-empty.
- static void cancel_wait();
+ void cancel_wait_impl();
// Pushes a deduplication candidate onto a specific GC worker queue.
- static void push(uint worker_id, oop java_string);
+ void push_impl(uint worker_id, oop java_string);
// Pops a deduplication candidate from any queue, returns NULL if
// all queues are empty.
- static oop pop();
+ oop pop_impl();
- static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl);
+ size_t num_queues() const {
+ return _nqueues;
+ }
- static void print_statistics();
- static void verify();
+ void unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue);
+
+ void print_statistics_impl();
+ void verify_impl();
};
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPQUEUE_HPP
--- a/src/hotspot/share/gc/g1/g1StringDedupStat.cpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedupStat.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -23,121 +23,60 @@
*/
#include "precompiled.hpp"
+
+#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1StringDedupStat.hpp"
#include "logging/log.hpp"
-G1StringDedupStat::G1StringDedupStat() :
- _inspected(0),
- _skipped(0),
- _hashed(0),
- _known(0),
- _new(0),
- _new_bytes(0),
- _deduped(0),
- _deduped_bytes(0),
+G1StringDedupStat::G1StringDedupStat() : StringDedupStat(),
_deduped_young(0),
_deduped_young_bytes(0),
_deduped_old(0),
_deduped_old_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) {
+ _heap(G1CollectedHeap::heap()) {
}
-void G1StringDedupStat::add(const G1StringDedupStat& stat) {
- _inspected += stat._inspected;
- _skipped += stat._skipped;
- _hashed += stat._hashed;
- _known += stat._known;
- _new += stat._new;
- _new_bytes += stat._new_bytes;
- _deduped += stat._deduped;
- _deduped_bytes += stat._deduped_bytes;
- _deduped_young += stat._deduped_young;
- _deduped_young_bytes += stat._deduped_young_bytes;
- _deduped_old += stat._deduped_old;
- _deduped_old_bytes += stat._deduped_old_bytes;
- _idle += stat._idle;
- _exec += stat._exec;
- _block += stat._block;
- _idle_elapsed += stat._idle_elapsed;
- _exec_elapsed += stat._exec_elapsed;
- _block_elapsed += stat._block_elapsed;
-}
+
-void G1StringDedupStat::print_start(const G1StringDedupStat& last_stat) {
- log_info(gc, stringdedup)(
- "Concurrent String Deduplication (" G1_STRDEDUP_TIME_FORMAT ")",
- G1_STRDEDUP_TIME_PARAM(last_stat._start_concurrent));
+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::print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
- double total_deduped_bytes_percent = 0.0;
-
- if (total_stat._new_bytes > 0) {
- // Avoid division by zero
- total_deduped_bytes_percent = percent_of(total_stat._deduped_bytes, total_stat._new_bytes);
- }
-
- log_info(gc, stringdedup)(
- "Concurrent String Deduplication "
- G1_STRDEDUP_BYTES_FORMAT_NS "->" G1_STRDEDUP_BYTES_FORMAT_NS "(" G1_STRDEDUP_BYTES_FORMAT_NS ") "
- "avg " G1_STRDEDUP_PERCENT_FORMAT_NS " "
- "(" G1_STRDEDUP_TIME_FORMAT ", " G1_STRDEDUP_TIME_FORMAT ") " G1_STRDEDUP_TIME_FORMAT_MS,
- G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes),
- G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes - last_stat._deduped_bytes),
- G1_STRDEDUP_BYTES_PARAM(last_stat._deduped_bytes),
- total_deduped_bytes_percent,
- G1_STRDEDUP_TIME_PARAM(last_stat._start_concurrent),
- G1_STRDEDUP_TIME_PARAM(last_stat._end_concurrent),
- G1_STRDEDUP_TIME_PARAM_MS(last_stat._exec_elapsed));
+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(const G1StringDedupStat& stat, bool total) {
- double skipped_percent = percent_of(stat._skipped, stat._inspected);
- double hashed_percent = percent_of(stat._hashed, stat._inspected);
- double known_percent = percent_of(stat._known, stat._inspected);
- double new_percent = percent_of(stat._new, stat._inspected);
- double deduped_percent = percent_of(stat._deduped, stat._new);
- double deduped_bytes_percent = percent_of(stat._deduped_bytes, stat._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);
+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);
- if (total) {
- log_debug(gc, stringdedup)(
- " Total Exec: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS
- ", Idle: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS
- ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS,
- stat._exec, G1_STRDEDUP_TIME_PARAM_MS(stat._exec_elapsed),
- stat._idle, G1_STRDEDUP_TIME_PARAM_MS(stat._idle_elapsed),
- stat._block, G1_STRDEDUP_TIME_PARAM_MS(stat._block_elapsed));
- } else {
- log_debug(gc, stringdedup)(
- " Last Exec: " G1_STRDEDUP_TIME_FORMAT_MS
- ", Idle: " G1_STRDEDUP_TIME_FORMAT_MS
- ", Blocked: " UINTX_FORMAT "/" G1_STRDEDUP_TIME_FORMAT_MS,
- G1_STRDEDUP_TIME_PARAM_MS(stat._exec_elapsed),
- G1_STRDEDUP_TIME_PARAM_MS(stat._idle_elapsed),
- stat._block, G1_STRDEDUP_TIME_PARAM_MS(stat._block_elapsed));
- }
- log_debug(gc, stringdedup)(" Inspected: " G1_STRDEDUP_OBJECTS_FORMAT, stat._inspected);
- log_debug(gc, stringdedup)(" Skipped: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._skipped, skipped_percent);
- log_debug(gc, stringdedup)(" Hashed: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._hashed, hashed_percent);
- log_debug(gc, stringdedup)(" Known: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")", stat._known, known_percent);
- log_debug(gc, stringdedup)(" New: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT,
- stat._new, new_percent, G1_STRDEDUP_BYTES_PARAM(stat._new_bytes));
- log_debug(gc, stringdedup)(" Deduplicated: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")",
- stat._deduped, deduped_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_bytes), deduped_bytes_percent);
- log_debug(gc, stringdedup)(" Young: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")",
- stat._deduped_young, deduped_young_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_young_bytes), deduped_young_bytes_percent);
- log_debug(gc, stringdedup)(" Old: " G1_STRDEDUP_OBJECTS_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ") " G1_STRDEDUP_BYTES_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT ")",
- stat._deduped_old, deduped_old_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_old_bytes), deduped_old_bytes_percent);
+ 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;
+}
--- a/src/hotspot/share/gc/g1/g1StringDedupStat.hpp Thu Jun 14 15:27:49 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1StringDedupStat.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,126 +25,28 @@
#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP
#define SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP
-#include "memory/allocation.hpp"
-#include "runtime/os.hpp"
-
-// Macros for GC log output formating
-#define G1_STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12)
-#define G1_STRDEDUP_TIME_FORMAT "%.3fs"
-#define G1_STRDEDUP_TIME_PARAM(time) (time)
-#define G1_STRDEDUP_TIME_FORMAT_MS "%.3fms"
-#define G1_STRDEDUP_TIME_PARAM_MS(time) ((time) * MILLIUNITS)
-#define G1_STRDEDUP_PERCENT_FORMAT "%5.1f%%"
-#define G1_STRDEDUP_PERCENT_FORMAT_NS "%.1f%%"
-#define G1_STRDEDUP_BYTES_FORMAT "%8.1f%s"
-#define G1_STRDEDUP_BYTES_FORMAT_NS "%.1f%s"
-#define G1_STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes))
+#include "gc/shared/stringdedup/stringDedupStat.hpp"
-//
-// Statistics gathered by the deduplication thread.
-//
-class G1StringDedupStat : public StackObj {
+// G1 extension for gathering/reporting generational statistics
+class G1StringDedupStat : public StringDedupStat {
private:
- // Counters
- uintx _inspected;
- uintx _skipped;
- uintx _hashed;
- uintx _known;
- uintx _new;
- uintx _new_bytes;
- uintx _deduped;
- uintx _deduped_bytes;
uintx _deduped_young;
uintx _deduped_young_bytes;
uintx _deduped_old;
uintx _deduped_old_bytes;
- uintx _idle;
- uintx _exec;
- uintx _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;
+ G1CollectedHeap* const _heap;
public:
G1StringDedupStat();
- void inc_inspected() {
- _inspected++;
- }
-
- void inc_skipped() {
- _skipped++;
- }
-
- void inc_hashed() {
- _hashed++;
- }
-
- void inc_known() {
- _known++;
- }
+ void deduped(oop obj, uintx bytes);
- void inc_new(uintx bytes) {
- _new++;
- _new_bytes += bytes;
- }
-
- void inc_deduped_young(uintx bytes) {
- _deduped++;
- _deduped_bytes += bytes;
- _deduped_young++;
- _deduped_young_bytes += bytes;
- }
-
- void inc_deduped_old(uintx bytes) {
- _deduped++;
- _deduped_bytes += bytes;
- _deduped_old++;
- _deduped_old_bytes += bytes;
- }
+ void add(const StringDedupStat* const stat);
- void mark_idle() {
- _start_phase = os::elapsedTime();
- _idle++;
- }
-
- void mark_exec() {
- double now = os::elapsedTime();
- _idle_elapsed = now - _start_phase;
- _start_phase = now;
- _start_concurrent = now;
- _exec++;
- }
+ void print_statistics(bool total) const;
- void mark_block() {
- double now = os::elapsedTime();
- _exec_elapsed += now - _start_phase;
- _start_phase = now;
- _block++;
- }
-
- void mark_unblock() {
- double now = os::elapsedTime();
- _block_elapsed += now - _start_phase;
- _start_phase = now;
- }
-
- void mark_done() {
- double now = os::elapsedTime();
- _exec_elapsed += now - _start_phase;
- _end_concurrent = now;
- }
-
- void add(const G1StringDedupStat& stat);
-
- static void print_start(const G1StringDedupStat& last_stat);
- static void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
- static void print_statistics(const G1StringDedupStat& stat, bool total);
+ void reset();
};
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPSTAT_HPP
--- a/src/hotspot/share/gc/g1/g1StringDedupTable.cpp Thu Jun 14 15:27:49 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,625 +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/altHashing.hpp"
-#include "classfile/javaClasses.inline.hpp"
-#include "gc/g1/g1BarrierSet.hpp"
-#include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/g1/g1StringDedup.hpp"
-#include "gc/g1/g1StringDedupTable.hpp"
-#include "logging/log.hpp"
-#include "memory/padded.inline.hpp"
-#include "oops/arrayOop.inline.hpp"
-#include "oops/oop.inline.hpp"
-#include "oops/typeArrayOop.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/safepointVerifiers.hpp"
-
-//
-// List of deduplication table entries. Links table
-// entries together using their _next fields.
-//
-class G1StringDedupEntryList : public CHeapObj<mtGC> {
-private:
- G1StringDedupEntry* _list;
- size_t _length;
-
-public:
- G1StringDedupEntryList() :
- _list(NULL),
- _length(0) {
- }
-
- void add(G1StringDedupEntry* entry) {
- entry->set_next(_list);
- _list = entry;
- _length++;
- }
-
- G1StringDedupEntry* remove() {
- G1StringDedupEntry* entry = _list;
- if (entry != NULL) {
- _list = entry->next();
- _length--;
- }
- return entry;
- }
-
- G1StringDedupEntry* remove_all() {
- G1StringDedupEntry* list = _list;
- _list = NULL;
- return list;
- }
-
- size_t length() {
- return _length;
- }
-};
-
-//
-// Cache of deduplication table entries. This cache provides fast allocation and
-// reuse of table entries to lower the pressure on the underlying allocator.
-// But more importantly, it provides fast/deferred freeing of table entries. This
-// is important because freeing of table entries is done during stop-the-world
-// phases and it is not uncommon for large number of entries to be freed at once.
-// Tables entries that are freed during these phases are placed onto a freelist in
-// the cache. The deduplication thread, which executes in a concurrent phase, will
-// later reuse or free the underlying memory for these entries.
-//
-// The cache allows for single-threaded allocations and multi-threaded frees.
-// Allocations are synchronized by StringDedupTable_lock as part of a table
-// modification.
-//
-class G1StringDedupEntryCache : public CHeapObj<mtGC> {
-private:
- // One cache/overflow list per GC worker to allow lock less freeing of
- // entries while doing a parallel scan of the table. Using PaddedEnd to
- // avoid false sharing.
- size_t _nlists;
- size_t _max_list_length;
- PaddedEnd<G1StringDedupEntryList>* _cached;
- PaddedEnd<G1StringDedupEntryList>* _overflowed;
-
-public:
- G1StringDedupEntryCache(size_t max_size);
- ~G1StringDedupEntryCache();
-
- // Set max number of table entries to cache.
- void set_max_size(size_t max_size);
-
- // Get a table entry from the cache, or allocate a new entry if the cache is empty.
- G1StringDedupEntry* alloc();
-
- // Insert a table entry into the cache.
- void free(G1StringDedupEntry* entry, uint worker_id);
-
- // Returns current number of entries in the cache.
- size_t size();
-
- // Deletes overflowed entries.
- void delete_overflowed();
-};
-
-G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) :
- _nlists(ParallelGCThreads),
- _max_list_length(0),
- _cached(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)),
- _overflowed(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)) {
- set_max_size(max_size);
-}
-
-G1StringDedupEntryCache::~G1StringDedupEntryCache() {
- ShouldNotReachHere();
-}
-
-void G1StringDedupEntryCache::set_max_size(size_t size) {
- _max_list_length = size / _nlists;
-}
-
-G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
- for (size_t i = 0; i < _nlists; i++) {
- G1StringDedupEntry* entry = _cached[i].remove();
- if (entry != NULL) {
- return entry;
- }
- }
- return new G1StringDedupEntry();
-}
-
-void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
- assert(entry->obj() != NULL, "Double free");
- assert(worker_id < _nlists, "Invalid worker id");
-
- entry->set_obj(NULL);
- entry->set_hash(0);
-
- if (_cached[worker_id].length() < _max_list_length) {
- // Cache is not full
- _cached[worker_id].add(entry);
- } else {
- // Cache is full, add to overflow list for later deletion
- _overflowed[worker_id].add(entry);
- }
-}
-
-size_t G1StringDedupEntryCache::size() {
- size_t size = 0;
- for (size_t i = 0; i < _nlists; i++) {
- size += _cached[i].length();
- }
- return size;
-}
-
-void G1StringDedupEntryCache::delete_overflowed() {
- double start = os::elapsedTime();
- uintx count = 0;
-
- for (size_t i = 0; i < _nlists; i++) {
- G1StringDedupEntry* entry;
-
- {
- // The overflow list can be modified during safepoints, therefore
- // we temporarily join the suspendible thread set while removing
- // all entries from the list.
- SuspendibleThreadSetJoiner sts_join;
- entry = _overflowed[i].remove_all();
- }
-
- // Delete all entries
- while (entry != NULL) {
- G1StringDedupEntry* next = entry->next();
- delete entry;
- entry = next;
- count++;
- }
- }
-
- double end = os::elapsedTime();
- log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT_MS,
- count, G1_STRDEDUP_TIME_PARAM_MS(end - start));
-}
-
-G1StringDedupTable* G1StringDedupTable::_table = NULL;
-G1StringDedupEntryCache* G1StringDedupTable::_entry_cache = NULL;
-
-const size_t G1StringDedupTable::_min_size = (1 << 10); // 1024
-const size_t G1StringDedupTable::_max_size = (1 << 24); // 16777216
-const double G1StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load
-const double G1StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load
-const double G1StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size
-const uintx G1StringDedupTable::_rehash_multiple = 60; // Hash bucket has 60 times more collisions than expected
-const uintx G1StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor);
-
-uintx G1StringDedupTable::_entries_added = 0;
-uintx G1StringDedupTable::_entries_removed = 0;
-uintx G1StringDedupTable::_resize_count = 0;
-uintx G1StringDedupTable::_rehash_count = 0;
-
-G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) :
- _size(size),
- _entries(0),
- _grow_threshold((uintx)(size * _grow_load_factor)),
- _shrink_threshold((uintx)(size * _shrink_load_factor)),
- _rehash_needed(false),
- _hash_seed(hash_seed) {
- assert(is_power_of_2(size), "Table size must be a power of 2");
- _buckets = NEW_C_HEAP_ARRAY(G1StringDedupEntry*, _size, mtGC);
- memset(_buckets, 0, _size * sizeof(G1StringDedupEntry*));
-}
-
-G1StringDedupTable::~G1StringDedupTable() {
- FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets);
-}
-
-void G1StringDedupTable::create() {
- assert(_table == NULL, "One string deduplication table allowed");
- _entry_cache = new G1StringDedupEntryCache(_min_size * _max_cache_factor);
- _table = new G1StringDedupTable(_min_size);
-}
-
-void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list) {
- G1StringDedupEntry* entry = _entry_cache->alloc();
- entry->set_obj(value);
- entry->set_hash(hash);
- entry->set_latin1(latin1);
- entry->set_next(*list);
- *list = entry;
- _entries++;
-}
-
-void G1StringDedupTable::remove(G1StringDedupEntry** pentry, uint worker_id) {
- G1StringDedupEntry* entry = *pentry;
- *pentry = entry->next();
- _entry_cache->free(entry, worker_id);
-}
-
-void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest) {
- G1StringDedupEntry* entry = *pentry;
- *pentry = entry->next();
- unsigned int hash = entry->hash();
- size_t index = dest->hash_to_index(hash);
- G1StringDedupEntry** list = dest->bucket(index);
- entry->set_next(*list);
- *list = entry;
-}
-
-bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) {
- return (value1 == value2 ||
- (value1->length() == value2->length() &&
- (!memcmp(value1->base(T_BYTE),
- value2->base(T_BYTE),
- value1->length() * sizeof(jbyte)))));
-}
-
-typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned int hash,
- G1StringDedupEntry** list, uintx &count) {
- for (G1StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) {
- if (entry->hash() == hash && entry->latin1() == latin1) {
- typeArrayOop existing_value = entry->obj();
- if (equals(value, existing_value)) {
- // Match found
- return existing_value;
- }
- }
- count++;
- }
-
- // Not found
- return NULL;
-}
-
-typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) {
- size_t index = hash_to_index(hash);
- G1StringDedupEntry** list = bucket(index);
- uintx count = 0;
-
- // Lookup in list
- typeArrayOop existing_value = lookup(value, latin1, hash, list, count);
-
- // Check if rehash is needed
- if (count > _rehash_threshold) {
- _rehash_needed = true;
- }
-
- if (existing_value == NULL) {
- // Not found, add new entry
- add(value, latin1, hash, list);
-
- // Update statistics
- _entries_added++;
- }
-
- return existing_value;
-}
-
-unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) {
- unsigned int hash;
- int length = value->length();
- if (latin1) {
- const jbyte* data = (jbyte*)value->base(T_BYTE);
- if (use_java_hash()) {
- hash = java_lang_String::hash_code(data, length);
- } else {
- hash = AltHashing::murmur3_32(_table->_hash_seed, data, length);
- }
- } else {
- length /= sizeof(jchar) / sizeof(jbyte); // Convert number of bytes to number of chars
- const jchar* data = (jchar*)value->base(T_CHAR);
- if (use_java_hash()) {
- hash = java_lang_String::hash_code(data, length);
- } else {
- hash = AltHashing::murmur3_32(_table->_hash_seed, data, length);
- }
- }
-
- return hash;
-}
-
-void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
- assert(java_lang_String::is_instance(java_string), "Must be a string");
- NoSafepointVerifier nsv;
-
- stat.inc_inspected();
-
- typeArrayOop value = java_lang_String::value(java_string);
- if (value == NULL) {
- // String has no value
- stat.inc_skipped();
- return;
- }
-
- bool latin1 = java_lang_String::is_latin1(java_string);
- unsigned int hash = 0;
-
- if (use_java_hash()) {
- // Get hash code from cache
- hash = java_lang_String::hash(java_string);
- }
-
- if (hash == 0) {
- // Compute hash
- hash = hash_code(value, latin1);
- stat.inc_hashed();
-
- if (use_java_hash() && hash != 0) {
- // Store hash code in cache
- java_lang_String::set_hash(java_string, hash);
- }
- }
-
- typeArrayOop existing_value = lookup_or_add(value, latin1, hash);
- if (existing_value == value) {
- // Same value, already known
- stat.inc_known();
- return;
- }
-
- // Get size of value array
- uintx size_in_bytes = value->size() * HeapWordSize;
- stat.inc_new(size_in_bytes);
-
- if (existing_value != NULL) {
- // Enqueue the reference to make sure it is kept alive. Concurrent mark might
- // otherwise declare it dead if there are no other strong references to this object.
- G1BarrierSet::enqueue(existing_value);
-
- // Existing value found, deduplicate string
- java_lang_String::set_value(java_string, existing_value);
-
- if (G1CollectedHeap::heap()->is_in_young(value)) {
- stat.inc_deduped_young(size_in_bytes);
- } else {
- stat.inc_deduped_old(size_in_bytes);
- }
- }
-}
-
-G1StringDedupTable* G1StringDedupTable::prepare_resize() {
- size_t size = _table->_size;
-
- // Check if the hashtable needs to be resized
- if (_table->_entries > _table->_grow_threshold) {
- // Grow table, double the size
- size *= 2;
- if (size > _max_size) {
- // Too big, don't resize
- return NULL;
- }
- } else if (_table->_entries < _table->_shrink_threshold) {
- // Shrink table, half the size
- size /= 2;
- if (size < _min_size) {
- // Too small, don't resize
- return NULL;
- }
- } else if (StringDeduplicationResizeALot) {
- // Force grow
- size *= 2;
- if (size > _max_size) {
- // Too big, force shrink instead
- size /= 4;
- }
- } else {
- // Resize not needed
- return NULL;
- }
-
- // Update statistics
- _resize_count++;
-
- // Update max cache size
- _entry_cache->set_max_size(size * _max_cache_factor);
-
- // Allocate the new table. The new table will be populated by workers
- // calling unlink_or_oops_do() and finally installed by finish_resize().
- return new G1StringDedupTable(size, _table->_hash_seed);
-}
-
-void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) {
- assert(resized_table != NULL, "Invalid table");
-
- resized_table->_entries = _table->_entries;
-
- // Free old table
- delete _table;
-
- // Install new table
- _table = resized_table;
-}
-
-void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) {
- // The table is divided into partitions to allow lock-less parallel processing by
- // multiple worker threads. A worker thread first claims a partition, which ensures
- // exclusive access to that part of the table, then continues to process it. To allow
- // shrinking of the table in parallel we also need to make sure that the same worker
- // thread processes all partitions where entries will hash to the same destination
- // partition. Since the table size is always a power of two and we always shrink by
- // dividing the table in half, we know that for a given partition there is only one
- // other partition whoes entries will hash to the same destination partition. That
- // other partition is always the sibling partition in the second half of the table.
- // For example, if the table is divided into 8 partitions, the sibling of partition 0
- // is partition 4, the sibling of partition 1 is partition 5, etc.
- size_t table_half = _table->_size / 2;
-
- // Let each partition be one page worth of buckets
- size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(G1StringDedupEntry*));
- assert(table_half % partition_size == 0, "Invalid partition size");
-
- // Number of entries removed during the scan
- uintx removed = 0;
-
- for (;;) {
- // Grab next partition to scan
- size_t partition_begin = cl->claim_table_partition(partition_size);
- size_t partition_end = partition_begin + partition_size;
- if (partition_begin >= table_half) {
- // End of table
- break;
- }
-
- // Scan the partition followed by the sibling partition in the second half of the table
- removed += unlink_or_oops_do(cl, partition_begin, partition_end, worker_id);
- removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id);
- }
-
- // Delayed update to avoid contention on the table lock
- if (removed > 0) {
- MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
- _table->_entries -= removed;
- _entries_removed += removed;
- }
-}
-
-uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
- size_t partition_begin,
- size_t partition_end,
- uint worker_id) {
- uintx removed = 0;
- for (size_t bucket = partition_begin; bucket < partition_end; bucket++) {
- G1StringDedupEntry** entry = _table->bucket(bucket);
- while (*entry != NULL) {
- oop* p = (oop*)(*entry)->obj_addr();
- if (cl->is_alive(*p)) {
- cl->keep_alive(p);
- if (cl->is_resizing()) {
- // We are resizing the table, transfer entry to the new table
- _table->transfer(entry, cl->resized_table());
- } else {
- if (cl->is_rehashing()) {
- // We are rehashing the table, rehash the entry but keep it
- // in the table. We can't transfer entries into the new table
- // at this point since we don't have exclusive access to all
- // destination partitions. finish_rehash() will do a single
- // threaded transfer of all entries.
- typeArrayOop value = (typeArrayOop)*p;
- bool latin1 = (*entry)->latin1();
- unsigned int hash = hash_code(value, latin1);
- (*entry)->set_hash(hash);
- }
-
- // Move to next entry
- entry = (*entry)->next_addr();
- }
- } else {
- // Not alive, remove entry from table
- _table->remove(entry, worker_id);
- removed++;
- }
- }
- }
-
- return removed;
-}
-
-G1StringDedupTable* G1StringDedupTable::prepare_rehash() {
- if (!_table->_rehash_needed && !StringDeduplicationRehashALot) {
- // Rehash not needed
- return NULL;
- }
-
- // Update statistics
- _rehash_count++;
-
- // Compute new hash seed
- _table->_hash_seed = AltHashing::compute_seed();
-
- // Allocate the new table, same size and hash seed
- return new G1StringDedupTable(_table->_size, _table->_hash_seed);
-}
-
-void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) {
- assert(rehashed_table != NULL, "Invalid table");
-
- // Move all newly rehashed entries into the correct buckets in the new table
- for (size_t bucket = 0; bucket < _table->_size; bucket++) {
- G1StringDedupEntry** entry = _table->bucket(bucket);
- while (*entry != NULL) {
- _table->transfer(entry, rehashed_table);
- }
- }
-
- rehashed_table->_entries = _table->_entries;
-
- // Free old table
- delete _table;
-
- // Install new table
- _table = rehashed_table;
-}
-
-void G1StringDedupTable::verify() {
- for (size_t bucket = 0; bucket < _table->_size; bucket++) {
- // Verify entries
- G1StringDedupEntry** entry = _table->bucket(bucket);
- while (*entry != NULL) {
- typeArrayOop value = (*entry)->obj();
- guarantee(value != NULL, "Object must not be NULL");
- guarantee(G1CollectedHeap::heap()->is_in_reserved(value), "Object must be on the heap");
- guarantee(!value->is_forwarded(), "Object must not be forwarded");
- guarantee(value->is_typeArray(), "Object must be a typeArrayOop");
- bool latin1 = (*entry)->latin1();
- unsigned int hash = hash_code(value, latin1);
- guarantee((*entry)->hash() == hash, "Table entry has inorrect hash");
- guarantee(_table->hash_to_index(hash) == bucket, "Table entry has incorrect index");
- entry = (*entry)->next_addr();
- }
-
- // Verify that we do not have entries with identical oops or identical arrays.
- // We only need to compare entries in the same bucket. If the same oop or an
- // identical array has been inserted more than once into different/incorrect
- // buckets the verification step above will catch that.
- G1StringDedupEntry** entry1 = _table->bucket(bucket);
- while (*entry1 != NULL) {
- typeArrayOop value1 = (*entry1)->obj();
- bool latin1_1 = (*entry1)->latin1();
- G1StringDedupEntry** entry2 = (*entry1)->next_addr();
- while (*entry2 != NULL) {
- typeArrayOop value2 = (*entry2)->obj();
- bool latin1_2 = (*entry2)->latin1();
- guarantee(latin1_1 != latin1_2 || !equals(value1, value2), "Table entries must not have identical arrays");
- entry2 = (*entry2)->next_addr();
- }
- entry1 = (*entry1)->next_addr();
- }
- }
-}
-
-void G1StringDedupTable::clean_entry_cache() {
- _entry_cache->delete_overflowed();
-}
-
-void G1StringDedupTable::print_statistics() {
- Log(gc, stringdedup) log;
- log.debug(" Table");
- log.debug(" Memory Usage: " G1_STRDEDUP_BYTES_FORMAT_NS,
- G1_STRDEDUP_BYTES_PARAM(_table->_size * sizeof(G1StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(G1StringDedupEntry)));
- log.debug(" Size: " SIZE_FORMAT ", Min: " SIZE_FORMAT ", Max: " SIZE_FORMAT, _table->_size, _min_size, _max_size);
- log.debug(" Entries: " UINTX_FORMAT ", Load: " G1_STRDEDUP_PERCENT_FORMAT_NS ", Cached: " UINTX_FORMAT ", Added: " UINTX_FORMAT ", Removed: " UINTX_FORMAT,
- _table->_entries, percent_of(_table->_entries, _table->_size), _entry_cache->size(), _entries_added, _entries_removed);
- log.debug(" Resize Count: " UINTX_FORMAT ", Shrink Threshold: " UINTX_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT_NS ")",
- _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0);
- log.debug(" Rehash Count: " UINTX_FORMAT ", Rehash Threshold: " UINTX_FORMAT ", Hash Seed: 0x%x", _rehash_count, _rehash_threshold, _table->_hash_seed);
- log.debug(" Age Threshold: " UINTX_FORMAT, StringDeduplicationAgeThreshold);
-}
--- a/src/hotspot/share/gc/g1/g1StringDedupTable.hpp Thu Jun 14 15:27:49 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, 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_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
-#define SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
-
-#include "gc/g1/g1StringDedupStat.hpp"
-#include "runtime/mutexLocker.hpp"
-
-class G1StringDedupEntryCache;
-class G1StringDedupUnlinkOrOopsDoClosure;
-
-//
-// 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.
-//
-class G1StringDedupEntry : public CHeapObj<mtGC> {
-private:
- G1StringDedupEntry* _next;
- unsigned int _hash;
- bool _latin1;
- typeArrayOop _obj;
-
-public:
- G1StringDedupEntry() :
- _next(NULL),
- _hash(0),
- _latin1(false),
- _obj(NULL) {
- }
-
- G1StringDedupEntry* next() {
- return _next;
- }
-
- G1StringDedupEntry** next_addr() {
- return &_next;
- }
-
- void set_next(G1StringDedupEntry* next) {
- _next = next;
- }
-
- unsigned int hash() {
- return _hash;
- }
-
- void set_hash(unsigned int hash) {
- _hash = hash;
- }
-
- bool latin1() {
- return _latin1;
- }
-
- void set_latin1(bool latin1) {
- _latin1 = latin1;
- }
-
- 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 G1StringDedupTable : public CHeapObj<mtGC> {
-private:
- // The currently active hashtable instance. Only modified when
- // the table is resizes or rehashed.
- static G1StringDedupTable* _table;
-
- // Cache for reuse and fast alloc/free of table entries.
- static G1StringDedupEntryCache* _entry_cache;
-
- G1StringDedupEntry** _buckets;
- size_t _size;
- 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.
- jint _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 uintx _entries_removed;
- static uintx _resize_count;
- static uintx _rehash_count;
-
- G1StringDedupTable(size_t size, jint hash_seed = 0);
- ~G1StringDedupTable();
-
- // Returns the hash bucket at the given index.
- G1StringDedupEntry** 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, G1StringDedupEntry** list);
-
- // Removes the given table entry from the table.
- void remove(G1StringDedupEntry** pentry, uint worker_id);
-
- // Transfers a table entry from the current table to the destination table.
- void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* 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,
- G1StringDedupEntry** 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.
- MutexLockerEx 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;
- }
-
- static bool equals(typeArrayOop value1, typeArrayOop value2);
-
- // 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(G1StringDedupUnlinkOrOopsDoClosure* cl,
- size_t partition_begin,
- size_t partition_end,
- uint worker_id);
-
-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, G1StringDedupStat& stat);
-
- // If a table resize is needed, returns a newly allocated empty
- // hashtable of the proper size.
- static G1StringDedupTable* prepare_resize();
-
- // Installs a newly resized table as the currently active table
- // and deletes the previously active table.
- static void finish_resize(G1StringDedupTable* resized_table);
-
- // If a table rehash is needed, returns a newly allocated empty
- // hashtable and updates the hash seed.
- static G1StringDedupTable* 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(G1StringDedupTable* rehashed_table);
-
- // If the table entry cache has grown too large, delete overflowed entries.
- static void clean_entry_cache();
-
- static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
-
- static void print_statistics();
- static void verify();
-};
-
-#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
--- a/src/hotspot/share/gc/g1/g1StringDedupThread.cpp Thu Jun 14 15:27:49 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, 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/g1/g1StringDedup.hpp"
-#include "gc/g1/g1StringDedupQueue.hpp"
-#include "gc/g1/g1StringDedupTable.hpp"
-#include "gc/g1/g1StringDedupThread.hpp"
-#include "gc/shared/suspendibleThreadSet.hpp"
-#include "logging/log.hpp"
-#include "oops/access.inline.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/atomic.hpp"
-
-G1StringDedupThread* G1StringDedupThread::_thread = NULL;
-
-G1StringDedupThread::G1StringDedupThread() :
- ConcurrentGCThread() {
- set_name("G1 StrDedup");
- create_and_start();
-}
-
-G1StringDedupThread::~G1StringDedupThread() {
- ShouldNotReachHere();
-}
-
-void G1StringDedupThread::create() {
- assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
- assert(_thread == NULL, "One string deduplication thread allowed");
- _thread = new G1StringDedupThread();
-}
-
-G1StringDedupThread* G1StringDedupThread::thread() {
- assert(G1StringDedup::is_enabled(), "String deduplication not enabled");
- assert(_thread != NULL, "String deduplication thread not created");
- return _thread;
-}
-
-class G1StringDedupSharedClosure: public OopClosure {
- private:
- G1StringDedupStat& _stat;
-
- public:
- G1StringDedupSharedClosure(G1StringDedupStat& stat) : _stat(stat) {}
-
- virtual void do_oop(oop* p) { ShouldNotReachHere(); }
- virtual void do_oop(narrowOop* p) {
- oop java_string = RawAccess<>::oop_load(p);
- G1StringDedupTable::deduplicate(java_string, _stat);
- }
-};
-
-// The CDS archive does not include the string dedupication table. Only the string
-// table is saved in the archive. The shared strings from CDS archive need to be
-// added to the string dedupication table before deduplication occurs. That is
-// done in the begining of the G1StringDedupThread (see G1StringDedupThread::run()
-// below).
-void G1StringDedupThread::deduplicate_shared_strings(G1StringDedupStat& stat) {
- G1StringDedupSharedClosure sharedStringDedup(stat);
- StringTable::shared_oops_do(&sharedStringDedup);
-}
-
-void G1StringDedupThread::run_service() {
- G1StringDedupStat total_stat;
-
- deduplicate_shared_strings(total_stat);
-
- // Main loop
- for (;;) {
- G1StringDedupStat stat;
-
- stat.mark_idle();
-
- // Wait for the queue to become non-empty
- G1StringDedupQueue::wait();
- if (should_terminate()) {
- break;
- }
-
- {
- // Include thread in safepoints
- SuspendibleThreadSetJoiner sts_join;
-
- stat.mark_exec();
- print_start(stat);
-
- // Process the queue
- for (;;) {
- oop java_string = G1StringDedupQueue::pop();
- if (java_string == NULL) {
- break;
- }
-
- G1StringDedupTable::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);
- }
-
- G1StringDedupTable::clean_entry_cache();
- }
-}
-
-void G1StringDedupThread::stop_service() {
- G1StringDedupQueue::cancel_wait();
-}
-
-void G1StringDedupThread::print_start(const G1StringDedupStat& last_stat) {
- G1StringDedupStat::print_start(last_stat);
-}
-
-void G1StringDedupThread::print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) {
- G1StringDedupStat::print_end(last_stat, total_stat);
- if (log_is_enabled(Debug, gc, stringdedup)) {
- G1StringDedupStat::print_statistics(last_stat, false);
- G1StringDedupStat::print_statistics(total_stat, true);
- G1StringDedupTable::print_statistics();
- G1StringDedupQueue::print_statistics();
- }
-}
--- a/src/hotspot/share/gc/g1/g1StringDedupThread.hpp Thu Jun 14 15:27:49 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, 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_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
-#define SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
-
-#include "gc/g1/g1StringDedupStat.hpp"
-#include "gc/shared/concurrentGCThread.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 G1StringDedupThread: public ConcurrentGCThread {
-private:
- static G1StringDedupThread* _thread;
-
- G1StringDedupThread();
- ~G1StringDedupThread();
-
- void print_start(const G1StringDedupStat& last_stat);
- void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
-
- void run_service();
- void stop_service();
-
-public:
- static void create();
-
- static G1StringDedupThread* thread();
-
- void deduplicate_shared_strings(G1StringDedupStat& stat);
-};
-
-#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,84 @@
+/*
+ * 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 "gc/shared/stringdedup/stringDedup.hpp"
+#include "gc/shared/stringdedup/stringDedupQueue.hpp"
+#include "gc/shared/stringdedup/stringDedupTable.hpp"
+#include "gc/shared/stringdedup/stringDedupThread.hpp"
+
+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);
+
+}
+void StringDedup::gc_epilogue() {
+ assert(is_enabled(), "String deduplication not enabled");
+ StringDedupQueue::gc_epilogue();
+ StringDedupTable::gc_epilogue();
+}
+
+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);
+}
+
+void StringDedup::threads_do(ThreadClosure* tc) {
+ assert(is_enabled(), "String deduplication not enabled");
+ tc->do_thread(StringDedupThread::thread());
+}
+
+void StringDedup::print_worker_threads_on(outputStream* st) {
+ assert(is_enabled(), "String deduplication not enabled");
+ StringDedupThread::thread()->print_on(st);
+ st->cr();
+}
+
+void StringDedup::verify() {
+ assert(is_enabled(), "String deduplication not enabled");
+ StringDedupQueue::verify();
+ StringDedupTable::verify();
+}
+
+
+StringDedupUnlinkOrOopsDoClosure::StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
+ OopClosure* keep_alive) :
+ _is_alive(is_alive), _keep_alive(keep_alive) {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP
+#define SHARE_VM_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.
+//
+// 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.
+//
+// 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.
+//
+// Candidate selection criteria is GC specific.
+//
+// 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.
+//
+// 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"
+
+//
+// Main interface for interacting with string deduplication.
+//
+class StringDedup : public AllStatic {
+private:
+ // Single state for checking if string deduplication is enabled.
+ static bool _enabled;
+
+public:
+ // Returns true if string deduplication is enabled.
+ static bool is_enabled() {
+ return _enabled;
+ }
+
+ // Stop the deduplication thread.
+ 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);
+
+ static void threads_do(ThreadClosure* tc);
+ static void print_worker_threads_on(outputStream* st);
+ static void verify();
+
+ // GC support
+ static void gc_prologue(bool resize_and_rehash_table);
+ static void gc_epilogue();
+
+protected:
+ // Initialize string deduplication.
+ // QUEUE: String Dedup Queue implementation
+ // STAT: String Dedup Stat implementation
+ template <typename QUEUE, typename STAT>
+ static void initialize_impl();
+};
+
+//
+// This closure encapsulates the closures needed when scanning
+// the deduplication queue and table during the unlink_or_oops_do() operation.
+//
+class StringDedupUnlinkOrOopsDoClosure : public StackObj {
+private:
+ BoolObjectClosure* _is_alive;
+ OopClosure* _keep_alive;
+
+public:
+ StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
+ OopClosure* keep_alive);
+
+ // Applies and returns the result from the is_alive closure, or
+ // returns true if no such closure was provided.
+ bool is_alive(oop o) {
+ if (_is_alive != NULL) {
+ return _is_alive->do_object_b(o);
+ }
+ return true;
+ }
+
+ // Applies the keep_alive closure, or does nothing if no such
+ // closure was provided.
+ void keep_alive(oop* p) {
+ if (_keep_alive != NULL) {
+ _keep_alive->do_oop(p);
+ }
+ }
+};
+
+#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.inline.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP
+#define SHARE_VM_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_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUP_INLINE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,66 @@
+/*
+ * 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::add(size_t(1), &_claimed_index) - 1;
+}
+
+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");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP
+#define SHARE_VM_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_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.inline.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP
+#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP
+
+#include "gc/shared/stringdedup/stringDedup.hpp"
+#include "gc/shared/stringdedup/stringDedupQueue.hpp"
+
+template <typename Q>
+void StringDedupQueue::create() {
+ assert(StringDedup::is_enabled(), "Must be enabled");
+ assert(_queue == NULL, "Can have only one queue");
+ _queue = new Q;
+}
+
+void StringDedupQueue::wait() {
+ queue()->wait_impl();
+}
+
+void StringDedupQueue::cancel_wait() {
+ queue()->cancel_wait_impl();
+}
+
+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_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPQUEUE_INLINE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,152 @@
+/*
+ * 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 "gc/shared/stringdedup/stringDedupStat.hpp"
+#include "logging/log.hpp"
+
+StringDedupStat::StringDedupStat() :
+ _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 StringDedupStat::add(const StringDedupStat* const stat) {
+ _inspected += stat->_inspected;
+ _skipped += stat->_skipped;
+ _hashed += stat->_hashed;
+ _known += stat->_known;
+ _new += stat->_new;
+ _new_bytes += stat->_new_bytes;
+ _deduped += stat->_deduped;
+ _deduped_bytes += stat->_deduped_bytes;
+ _idle += stat->_idle;
+ _exec += stat->_exec;
+ _block += stat->_block;
+ _idle_elapsed += stat->_idle_elapsed;
+ _exec_elapsed += stat->_exec_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));
+}
+
+void StringDedupStat::print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat) {
+ double total_deduped_bytes_percent = 0.0;
+
+ if (total_stat->_new_bytes > 0) {
+ // Avoid division by zero
+ total_deduped_bytes_percent = percent_of(total_stat->_deduped_bytes, total_stat->_new_bytes);
+ }
+
+ log_info(gc, 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),
+ 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));
+}
+
+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 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));
+ }
+ 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);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP
+#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/os.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))
+
+//
+// Statistics gathered by the deduplication thread.
+//
+class StringDedupStat : public CHeapObj<mtGC> {
+protected:
+ // 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;
+
+ // 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;
+
+public:
+ StringDedupStat();
+
+ void inc_inspected() {
+ _inspected++;
+ }
+
+ void inc_skipped() {
+ _skipped++;
+ }
+
+ void inc_hashed() {
+ _hashed++;
+ }
+
+ void inc_known() {
+ _known++;
+ }
+
+ void inc_new(uintx bytes) {
+ _new++;
+ _new_bytes += bytes;
+ }
+
+ virtual void deduped(oop obj, uintx bytes) {
+ _deduped++;
+ _deduped_bytes += bytes;
+ }
+
+ void mark_idle() {
+ _start_phase = os::elapsedTime();
+ _idle++;
+ }
+
+ void mark_exec() {
+ double now = os::elapsedTime();
+ _idle_elapsed = now - _start_phase;
+ _start_phase = now;
+ _start_concurrent = now;
+ _exec++;
+ }
+
+ void mark_block() {
+ double now = os::elapsedTime();
+ _exec_elapsed += now - _start_phase;
+ _start_phase = now;
+ _block++;
+ }
+
+ void mark_unblock() {
+ double now = os::elapsedTime();
+ _block_elapsed += now - _start_phase;
+ _start_phase = now;
+ }
+
+ void mark_done() {
+ double now = os::elapsedTime();
+ _exec_elapsed += now - _start_phase;
+ _end_concurrent = now;
+ }
+
+ virtual void reset();
+ virtual void add(const StringDedupStat* const stat);
+ virtual void print_statistics(bool total) const;
+
+ static void print_start(const StringDedupStat* last_stat);
+ static void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat);
+};
+
+#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPSTAT_HPP
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,662 @@
+/*
+ * 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/altHashing.hpp"
+#include "classfile/javaClasses.inline.hpp"
+#include "gc/shared/stringdedup/stringDedup.hpp"
+#include "gc/shared/stringdedup/stringDedupTable.hpp"
+#include "gc/shared/suspendibleThreadSet.hpp"
+#include "logging/log.hpp"
+#include "memory/padded.inline.hpp"
+#include "oops/access.inline.hpp"
+#include "oops/arrayOop.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/typeArrayOop.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/safepointVerifiers.hpp"
+
+//
+// List of deduplication table entries. Links table
+// entries together using their _next fields.
+//
+class StringDedupEntryList : public CHeapObj<mtGC> {
+private:
+ StringDedupEntry* _list;
+ size_t _length;
+
+public:
+ StringDedupEntryList() :
+ _list(NULL),
+ _length(0) {
+ }
+
+ void add(StringDedupEntry* entry) {
+ entry->set_next(_list);
+ _list = entry;
+ _length++;
+ }
+
+ StringDedupEntry* remove() {
+ StringDedupEntry* entry = _list;
+ if (entry != NULL) {
+ _list = entry->next();
+ _length--;
+ }
+ return entry;
+ }
+
+ StringDedupEntry* remove_all() {
+ StringDedupEntry* list = _list;
+ _list = NULL;
+ return list;
+ }
+
+ size_t length() {
+ return _length;
+ }
+};
+
+//
+// Cache of deduplication table entries. This cache provides fast allocation and
+// reuse of table entries to lower the pressure on the underlying allocator.
+// But more importantly, it provides fast/deferred freeing of table entries. This
+// is important because freeing of table entries is done during stop-the-world
+// phases and it is not uncommon for large number of entries to be freed at once.
+// Tables entries that are freed during these phases are placed onto a freelist in
+// the cache. The deduplication thread, which executes in a concurrent phase, will
+// later reuse or free the underlying memory for these entries.
+//
+// The cache allows for single-threaded allocations and multi-threaded frees.
+// Allocations are synchronized by StringDedupTable_lock as part of a table
+// modification.
+//
+class StringDedupEntryCache : public CHeapObj<mtGC> {
+private:
+ // One cache/overflow list per GC worker to allow lock less freeing of
+ // entries while doing a parallel scan of the table. Using PaddedEnd to
+ // avoid false sharing.
+ size_t _nlists;
+ size_t _max_list_length;
+ PaddedEnd<StringDedupEntryList>* _cached;
+ PaddedEnd<StringDedupEntryList>* _overflowed;
+
+public:
+ StringDedupEntryCache(size_t max_size);
+ ~StringDedupEntryCache();
+
+ // Set max number of table entries to cache.
+ void set_max_size(size_t max_size);
+
+ // Get a table entry from the cache, or allocate a new entry if the cache is empty.
+ StringDedupEntry* alloc();
+
+ // Insert a table entry into the cache.
+ void free(StringDedupEntry* entry, uint worker_id);
+
+ // Returns current number of entries in the cache.
+ size_t size();
+
+ // Deletes overflowed entries.
+ void delete_overflowed();
+};
+
+StringDedupEntryCache::StringDedupEntryCache(size_t max_size) :
+ _nlists(ParallelGCThreads),
+ _max_list_length(0),
+ _cached(PaddedArray<StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)),
+ _overflowed(PaddedArray<StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)) {
+ set_max_size(max_size);
+}
+
+StringDedupEntryCache::~StringDedupEntryCache() {
+ ShouldNotReachHere();
+}
+
+void StringDedupEntryCache::set_max_size(size_t size) {
+ _max_list_length = size / _nlists;
+}
+
+StringDedupEntry* StringDedupEntryCache::alloc() {
+ for (size_t i = 0; i < _nlists; i++) {
+ StringDedupEntry* entry = _cached[i].remove();
+ if (entry != NULL) {
+ return entry;
+ }
+ }
+ return new StringDedupEntry();
+}
+
+void StringDedupEntryCache::free(StringDedupEntry* entry, uint worker_id) {
+ assert(entry->obj() != NULL, "Double free");
+ assert(worker_id < _nlists, "Invalid worker id");
+
+ entry->set_obj(NULL);
+ entry->set_hash(0);
+
+ if (_cached[worker_id].length() < _max_list_length) {
+ // Cache is not full
+ _cached[worker_id].add(entry);
+ } else {
+ // Cache is full, add to overflow list for later deletion
+ _overflowed[worker_id].add(entry);
+ }
+}
+
+size_t StringDedupEntryCache::size() {
+ size_t size = 0;
+ for (size_t i = 0; i < _nlists; i++) {
+ size += _cached[i].length();
+ }
+ return size;
+}
+
+void StringDedupEntryCache::delete_overflowed() {
+ double start = os::elapsedTime();
+ uintx count = 0;
+
+ for (size_t i = 0; i < _nlists; i++) {
+ StringDedupEntry* entry;
+
+ {
+ // The overflow list can be modified during safepoints, therefore
+ // we temporarily join the suspendible thread set while removing
+ // all entries from the list.
+ SuspendibleThreadSetJoiner sts_join;
+ entry = _overflowed[i].remove_all();
+ }
+
+ // Delete all entries
+ while (entry != NULL) {
+ StringDedupEntry* next = entry->next();
+ delete entry;
+ entry = next;
+ count++;
+ }
+ }
+
+ double end = os::elapsedTime();
+ log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " STRDEDUP_TIME_FORMAT_MS,
+ count, STRDEDUP_TIME_PARAM_MS(end - start));
+}
+
+StringDedupTable* StringDedupTable::_table = NULL;
+StringDedupEntryCache* StringDedupTable::_entry_cache = NULL;
+
+const size_t StringDedupTable::_min_size = (1 << 10); // 1024
+const size_t StringDedupTable::_max_size = (1 << 24); // 16777216
+const double StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load
+const double StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load
+const double StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size
+const uintx StringDedupTable::_rehash_multiple = 60; // Hash bucket has 60 times more collisions than expected
+const uintx StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor);
+
+uintx StringDedupTable::_entries_added = 0;
+uintx StringDedupTable::_entries_removed = 0;
+uintx StringDedupTable::_resize_count = 0;
+uintx StringDedupTable::_rehash_count = 0;
+
+StringDedupTable* StringDedupTable::_resized_table = NULL;
+StringDedupTable* StringDedupTable::_rehashed_table = NULL;
+volatile size_t StringDedupTable::_claimed_index = 0;
+
+StringDedupTable::StringDedupTable(size_t size, jint hash_seed) :
+ _size(size),
+ _entries(0),
+ _grow_threshold((uintx)(size * _grow_load_factor)),
+ _shrink_threshold((uintx)(size * _shrink_load_factor)),
+ _rehash_needed(false),
+ _hash_seed(hash_seed) {
+ assert(is_power_of_2(size), "Table size must be a power of 2");
+ _buckets = NEW_C_HEAP_ARRAY(StringDedupEntry*, _size, mtGC);
+ memset(_buckets, 0, _size * sizeof(StringDedupEntry*));
+}
+
+StringDedupTable::~StringDedupTable() {
+ FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets);
+}
+
+void StringDedupTable::create() {
+ assert(_table == NULL, "One string deduplication table allowed");
+ _entry_cache = new StringDedupEntryCache(_min_size * _max_cache_factor);
+ _table = new StringDedupTable(_min_size);
+}
+
+void StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, StringDedupEntry** list) {
+ StringDedupEntry* entry = _entry_cache->alloc();
+ entry->set_obj(value);
+ entry->set_hash(hash);
+ entry->set_latin1(latin1);
+ entry->set_next(*list);
+ *list = entry;
+ _entries++;
+}
+
+void StringDedupTable::remove(StringDedupEntry** pentry, uint worker_id) {
+ StringDedupEntry* entry = *pentry;
+ *pentry = entry->next();
+ _entry_cache->free(entry, worker_id);
+}
+
+void StringDedupTable::transfer(StringDedupEntry** pentry, StringDedupTable* dest) {
+ StringDedupEntry* entry = *pentry;
+ *pentry = entry->next();
+ unsigned int hash = entry->hash();
+ size_t index = dest->hash_to_index(hash);
+ StringDedupEntry** list = dest->bucket(index);
+ entry->set_next(*list);
+ *list = entry;
+}
+
+bool StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) {
+ return (oopDesc::equals(value1, value2) ||
+ (value1->length() == value2->length() &&
+ (!memcmp(value1->base(T_BYTE),
+ value2->base(T_BYTE),
+ value1->length() * sizeof(jbyte)))));
+}
+
+typeArrayOop StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigned int hash,
+ StringDedupEntry** list, uintx &count) {
+ for (StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) {
+ if (entry->hash() == hash && entry->latin1() == latin1) {
+ typeArrayOop existing_value = entry->obj();
+ if (equals(value, existing_value)) {
+ // Apply proper barrier to make sure it is kept alive. Concurrent mark might
+ // otherwise declare it dead if there are no other strong references to this object.
+ oop* obj_addr = (oop*)entry->obj_addr();
+ oop obj = RootAccess<IN_CONCURRENT_ROOT | ON_WEAK_OOP_REF>::oop_load(obj_addr);
+ return typeArrayOop(obj);
+ }
+ }
+ count++;
+ }
+
+ // Not found
+ return NULL;
+}
+
+typeArrayOop StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) {
+ size_t index = hash_to_index(hash);
+ StringDedupEntry** list = bucket(index);
+ uintx count = 0;
+
+ // Lookup in list
+ typeArrayOop existing_value = lookup(value, latin1, hash, list, count);
+
+ // Check if rehash is needed
+ if (count > _rehash_threshold) {
+ _rehash_needed = true;
+ }
+
+ if (existing_value == NULL) {
+ // Not found, add new entry
+ add(value, latin1, hash, list);
+
+ // Update statistics
+ _entries_added++;
+ }
+
+ return existing_value;
+}
+
+unsigned int StringDedupTable::hash_code(typeArrayOop value, bool latin1) {
+ unsigned int hash;
+ int length = value->length();
+ if (latin1) {
+ const jbyte* data = (jbyte*)value->base(T_BYTE);
+ if (use_java_hash()) {
+ hash = java_lang_String::hash_code(data, length);
+ } else {
+ hash = AltHashing::murmur3_32(_table->_hash_seed, data, length);
+ }
+ } else {
+ length /= sizeof(jchar) / sizeof(jbyte); // Convert number of bytes to number of chars
+ const jchar* data = (jchar*)value->base(T_CHAR);
+ if (use_java_hash()) {
+ hash = java_lang_String::hash_code(data, length);
+ } else {
+ hash = AltHashing::murmur3_32(_table->_hash_seed, data, length);
+ }
+ }
+
+ return hash;
+}
+
+void StringDedupTable::deduplicate(oop java_string, StringDedupStat* stat) {
+ assert(java_lang_String::is_instance(java_string), "Must be a string");
+ NoSafepointVerifier nsv;
+
+ stat->inc_inspected();
+
+ typeArrayOop value = java_lang_String::value(java_string);
+ if (value == NULL) {
+ // String has no value
+ stat->inc_skipped();
+ return;
+ }
+
+ bool latin1 = java_lang_String::is_latin1(java_string);
+ unsigned int hash = 0;
+
+ if (use_java_hash()) {
+ // Get hash code from cache
+ hash = java_lang_String::hash(java_string);
+ }
+
+ if (hash == 0) {
+ // Compute hash
+ hash = hash_code(value, latin1);
+ stat->inc_hashed();
+
+ if (use_java_hash() && hash != 0) {
+ // Store hash code in cache
+ java_lang_String::set_hash(java_string, hash);
+ }
+ }
+
+ typeArrayOop existing_value = lookup_or_add(value, latin1, hash);
+ if (existing_value == value) {
+ // Same value, already known
+ stat->inc_known();
+ return;
+ }
+
+ // Get size of value array
+ uintx size_in_bytes = value->size() * HeapWordSize;
+ stat->inc_new(size_in_bytes);
+
+ if (existing_value != NULL) {
+ // Existing value found, deduplicate string
+ java_lang_String::set_value(java_string, existing_value);
+ stat->deduped(value, size_in_bytes);
+ }
+}
+
+bool StringDedupTable::is_resizing() {
+ return _resized_table != NULL;
+}
+
+bool StringDedupTable::is_rehashing() {
+ return _rehashed_table != NULL;
+}
+
+StringDedupTable* StringDedupTable::prepare_resize() {
+ size_t size = _table->_size;
+
+ // Check if the hashtable needs to be resized
+ if (_table->_entries > _table->_grow_threshold) {
+ // Grow table, double the size
+ size *= 2;
+ if (size > _max_size) {
+ // Too big, don't resize
+ return NULL;
+ }
+ } else if (_table->_entries < _table->_shrink_threshold) {
+ // Shrink table, half the size
+ size /= 2;
+ if (size < _min_size) {
+ // Too small, don't resize
+ return NULL;
+ }
+ } else if (StringDeduplicationResizeALot) {
+ // Force grow
+ size *= 2;
+ if (size > _max_size) {
+ // Too big, force shrink instead
+ size /= 4;
+ }
+ } else {
+ // Resize not needed
+ return NULL;
+ }
+
+ // Update statistics
+ _resize_count++;
+
+ // Update max cache size
+ _entry_cache->set_max_size(size * _max_cache_factor);
+
+ // Allocate the new table. The new table will be populated by workers
+ // calling unlink_or_oops_do() and finally installed by finish_resize().
+ return new StringDedupTable(size, _table->_hash_seed);
+}
+
+void StringDedupTable::finish_resize(StringDedupTable* resized_table) {
+ assert(resized_table != NULL, "Invalid table");
+
+ resized_table->_entries = _table->_entries;
+
+ // Free old table
+ delete _table;
+
+ // Install new table
+ _table = resized_table;
+}
+
+void StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) {
+ // The table is divided into partitions to allow lock-less parallel processing by
+ // multiple worker threads. A worker thread first claims a partition, which ensures
+ // exclusive access to that part of the table, then continues to process it. To allow
+ // shrinking of the table in parallel we also need to make sure that the same worker
+ // thread processes all partitions where entries will hash to the same destination
+ // partition. Since the table size is always a power of two and we always shrink by
+ // dividing the table in half, we know that for a given partition there is only one
+ // other partition whoes entries will hash to the same destination partition. That
+ // other partition is always the sibling partition in the second half of the table.
+ // For example, if the table is divided into 8 partitions, the sibling of partition 0
+ // is partition 4, the sibling of partition 1 is partition 5, etc.
+ size_t table_half = _table->_size / 2;
+
+ // Let each partition be one page worth of buckets
+ size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(StringDedupEntry*));
+ assert(table_half % partition_size == 0, "Invalid partition size");
+
+ // Number of entries removed during the scan
+ uintx removed = 0;
+
+ for (;;) {
+ // Grab next partition to scan
+ size_t partition_begin = claim_table_partition(partition_size);
+ size_t partition_end = partition_begin + partition_size;
+ if (partition_begin >= table_half) {
+ // End of table
+ break;
+ }
+
+ // Scan the partition followed by the sibling partition in the second half of the table
+ removed += unlink_or_oops_do(cl, partition_begin, partition_end, worker_id);
+ removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id);
+ }
+
+ // Delayed update to avoid contention on the table lock
+ if (removed > 0) {
+ MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
+ _table->_entries -= removed;
+ _entries_removed += removed;
+ }
+}
+
+uintx StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl,
+ size_t partition_begin,
+ size_t partition_end,
+ uint worker_id) {
+ uintx removed = 0;
+ for (size_t bucket = partition_begin; bucket < partition_end; bucket++) {
+ StringDedupEntry** entry = _table->bucket(bucket);
+ while (*entry != NULL) {
+ oop* p = (oop*)(*entry)->obj_addr();
+ if (cl->is_alive(*p)) {
+ cl->keep_alive(p);
+ if (is_resizing()) {
+ // We are resizing the table, transfer entry to the new table
+ _table->transfer(entry, _resized_table);
+ } else {
+ if (is_rehashing()) {
+ // We are rehashing the table, rehash the entry but keep it
+ // in the table. We can't transfer entries into the new table
+ // at this point since we don't have exclusive access to all
+ // destination partitions. finish_rehash() will do a single
+ // threaded transfer of all entries.
+ typeArrayOop value = (typeArrayOop)*p;
+ bool latin1 = (*entry)->latin1();
+ unsigned int hash = hash_code(value, latin1);
+ (*entry)->set_hash(hash);
+ }
+
+ // Move to next entry
+ entry = (*entry)->next_addr();
+ }
+ } else {
+ // Not alive, remove entry from table
+ _table->remove(entry, worker_id);
+ removed++;
+ }
+ }
+ }
+
+ return removed;
+}
+
+void StringDedupTable::gc_prologue(bool resize_and_rehash_table) {
+ assert(!is_resizing() && !is_rehashing(), "Already in progress?");
+
+ _claimed_index = 0;
+ if (resize_and_rehash_table) {
+ // If both resize and rehash is needed, only do resize. Rehash of
+ // the table will eventually happen if the situation persists.
+ _resized_table = StringDedupTable::prepare_resize();
+ if (!is_resizing()) {
+ _rehashed_table = StringDedupTable::prepare_rehash();
+ }
+ }
+}
+
+void StringDedupTable::gc_epilogue() {
+ assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash");
+ assert(_claimed_index >= _table->_size / 2 || _claimed_index == 0, "All or nothing");
+
+ if (is_resizing()) {
+ StringDedupTable::finish_resize(_resized_table);
+ _resized_table = NULL;
+ } else if (is_rehashing()) {
+ StringDedupTable::finish_rehash(_rehashed_table);
+ _rehashed_table = NULL;
+ }
+}
+
+StringDedupTable* StringDedupTable::prepare_rehash() {
+ if (!_table->_rehash_needed && !StringDeduplicationRehashALot) {
+ // Rehash not needed
+ return NULL;
+ }
+
+ // Update statistics
+ _rehash_count++;
+
+ // Compute new hash seed
+ _table->_hash_seed = AltHashing::compute_seed();
+
+ // Allocate the new table, same size and hash seed
+ return new StringDedupTable(_table->_size, _table->_hash_seed);
+}
+
+void StringDedupTable::finish_rehash(StringDedupTable* rehashed_table) {
+ assert(rehashed_table != NULL, "Invalid table");
+
+ // Move all newly rehashed entries into the correct buckets in the new table
+ for (size_t bucket = 0; bucket < _table->_size; bucket++) {
+ StringDedupEntry** entry = _table->bucket(bucket);
+ while (*entry != NULL) {
+ _table->transfer(entry, rehashed_table);
+ }
+ }
+
+ rehashed_table->_entries = _table->_entries;
+
+ // Free old table
+ delete _table;
+
+ // Install new table
+ _table = rehashed_table;
+}
+
+size_t StringDedupTable::claim_table_partition(size_t partition_size) {
+ return Atomic::add(partition_size, &_claimed_index) - partition_size;
+}
+
+void StringDedupTable::verify() {
+ for (size_t bucket = 0; bucket < _table->_size; bucket++) {
+ // Verify entries
+ StringDedupEntry** entry = _table->bucket(bucket);
+ while (*entry != NULL) {
+ typeArrayOop value = (*entry)->obj();
+ guarantee(value != NULL, "Object must not be NULL");
+ guarantee(Universe::heap()->is_in_reserved(value), "Object must be on the heap");
+ guarantee(!value->is_forwarded(), "Object must not be forwarded");
+ guarantee(value->is_typeArray(), "Object must be a typeArrayOop");
+ bool latin1 = (*entry)->latin1();
+ unsigned int hash = hash_code(value, latin1);
+ guarantee((*entry)->hash() == hash, "Table entry has inorrect hash");
+ guarantee(_table->hash_to_index(hash) == bucket, "Table entry has incorrect index");
+ entry = (*entry)->next_addr();
+ }
+
+ // Verify that we do not have entries with identical oops or identical arrays.
+ // We only need to compare entries in the same bucket. If the same oop or an
+ // identical array has been inserted more than once into different/incorrect
+ // buckets the verification step above will catch that.
+ StringDedupEntry** entry1 = _table->bucket(bucket);
+ while (*entry1 != NULL) {
+ typeArrayOop value1 = (*entry1)->obj();
+ bool latin1_1 = (*entry1)->latin1();
+ StringDedupEntry** entry2 = (*entry1)->next_addr();
+ while (*entry2 != NULL) {
+ typeArrayOop value2 = (*entry2)->obj();
+ bool latin1_2 = (*entry2)->latin1();
+ guarantee(latin1_1 != latin1_2 || !equals(value1, value2), "Table entries must not have identical arrays");
+ entry2 = (*entry2)->next_addr();
+ }
+ entry1 = (*entry1)->next_addr();
+ }
+ }
+}
+
+void StringDedupTable::clean_entry_cache() {
+ _entry_cache->delete_overflowed();
+}
+
+void StringDedupTable::print_statistics() {
+ Log(gc, stringdedup) log;
+ log.debug(" Table");
+ log.debug(" Memory Usage: " STRDEDUP_BYTES_FORMAT_NS,
+ STRDEDUP_BYTES_PARAM(_table->_size * sizeof(StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(StringDedupEntry)));
+ log.debug(" Size: " SIZE_FORMAT ", Min: " SIZE_FORMAT ", Max: " SIZE_FORMAT, _table->_size, _min_size, _max_size);
+ log.debug(" Entries: " UINTX_FORMAT ", Load: " STRDEDUP_PERCENT_FORMAT_NS ", Cached: " UINTX_FORMAT ", Added: " UINTX_FORMAT ", Removed: " UINTX_FORMAT,
+ _table->_entries, percent_of(_table->_entries, _table->_size), _entry_cache->size(), _entries_added, _entries_removed);
+ log.debug(" Resize Count: " UINTX_FORMAT ", Shrink Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" STRDEDUP_PERCENT_FORMAT_NS ")",
+ _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0);
+ log.debug(" Rehash Count: " UINTX_FORMAT ", Rehash Threshold: " UINTX_FORMAT ", Hash Seed: 0x%x", _rehash_count, _rehash_threshold, _table->_hash_seed);
+ log.debug(" Age Threshold: " UINTX_FORMAT, StringDeduplicationAgeThreshold);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
+#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
+
+#include "gc/shared/stringdedup/stringDedupStat.hpp"
+#include "runtime/mutexLocker.hpp"
+
+class StringDedupEntryCache;
+class StringDedupUnlinkOrOopsDoClosure;
+
+//
+// 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.
+//
+class StringDedupEntry : public CHeapObj<mtGC> {
+private:
+ StringDedupEntry* _next;
+ unsigned int _hash;
+ bool _latin1;
+ typeArrayOop _obj;
+
+public:
+ StringDedupEntry() :
+ _next(NULL),
+ _hash(0),
+ _latin1(false),
+ _obj(NULL) {
+ }
+
+ StringDedupEntry* next() {
+ return _next;
+ }
+
+ StringDedupEntry** next_addr() {
+ return &_next;
+ }
+
+ void set_next(StringDedupEntry* next) {
+ _next = next;
+ }
+
+ unsigned int hash() {
+ return _hash;
+ }
+
+ void set_hash(unsigned int hash) {
+ _hash = hash;
+ }
+
+ bool latin1() {
+ return _latin1;
+ }
+
+ void set_latin1(bool latin1) {
+ _latin1 = latin1;
+ }
+
+ 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;
+ 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.
+ jint _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 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, jint 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.
+ MutexLockerEx 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;
+ }
+
+ static bool equals(typeArrayOop value1, typeArrayOop value2);
+
+ // 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();
+};
+
+#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.cpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,95 @@
+/*
+ * 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"
+#include "runtime/atomic.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(oop* p) { ShouldNotReachHere(); }
+ virtual void do_oop(narrowOop* p) {
+ oop java_string = RawAccess<>::oop_load(p);
+ StringDedupTable::deduplicate(java_string, _stat);
+ }
+};
+
+// The CDS archive does not include the string dedupication table. Only the string
+// table is saved in the archive. The shared strings from CDS archive need to be
+// added to the string dedupication table before deduplication occurs. That is
+// done in the begining 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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP
+#define SHARE_VM_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_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupThread.inline.hpp Thu Jun 14 09:59:21 2018 -0400
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP
+#define SHARE_VM_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;
+
+ 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_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_INLINE_HPP