8203641: Refactor String Deduplication into shared
Allows string deduplication to be shared among different collectors Reviewed-by: tschatzl, rkennke
This commit is contained in:
parent
4455892789
commit
f6c168053f
@ -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::enqueue_from_evacuation(bool from_young, bool to_young, uint
|
||||
}
|
||||
}
|
||||
|
||||
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::oops_do(OopClosure* keep_alive) {
|
||||
|
||||
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 @@ public:
|
||||
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 @@ void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive,
|
||||
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;
|
||||
}
|
||||
|
@ -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 G1StringDedupUnlinkOrOopsDoClosure;
|
||||
class G1GCPhaseTimes;
|
||||
class G1StringDedupUnlinkOrOopsDoClosure;
|
||||
|
||||
//
|
||||
// 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 @@ private:
|
||||
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 @@ public:
|
||||
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();
|
||||
|
||||
// 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);
|
||||
bool allow_resize_and_rehash) :
|
||||
StringDedupUnlinkOrOopsDoClosure(is_alive, keep_alive) {
|
||||
if (G1StringDedup::is_enabled()) {
|
||||
G1StringDedup::gc_prologue(allow_resize_and_rehash);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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 @@ G1StringDedupQueue::~G1StringDedupQueue() {
|
||||
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 @@ oop G1StringDedupQueue::pop() {
|
||||
}
|
||||
|
||||
// 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::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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).
|
||||
// G1 enqueues candidates during the stop-the-world mark/evacuation phase.
|
||||
//
|
||||
// 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), 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 @@ private:
|
||||
// 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
|
||||
|
@ -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::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::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);
|
||||
|
||||
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));
|
||||
void G1StringDedupStat::deduped(oop obj, uintx bytes) {
|
||||
StringDedupStat::deduped(obj, bytes);
|
||||
if (_heap->is_in_young(obj)) {
|
||||
_deduped_young ++;
|
||||
_deduped_young_bytes += bytes;
|
||||
} 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));
|
||||
_deduped_old ++;
|
||||
_deduped_old_bytes += bytes;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void G1StringDedupStat::add(const StringDedupStat* const stat) {
|
||||
StringDedupStat::add(stat);
|
||||
const G1StringDedupStat* const g1_stat = (const G1StringDedupStat* const)stat;
|
||||
_deduped_young += g1_stat->_deduped_young;
|
||||
_deduped_young_bytes += g1_stat->_deduped_young_bytes;
|
||||
_deduped_old += g1_stat->_deduped_old;
|
||||
_deduped_old_bytes += g1_stat->_deduped_old_bytes;
|
||||
}
|
||||
|
||||
void G1StringDedupStat::print_statistics(bool total) const {
|
||||
StringDedupStat::print_statistics(total);
|
||||
|
||||
double deduped_young_percent = percent_of(_deduped_young, _deduped);
|
||||
double deduped_young_bytes_percent = percent_of(_deduped_young_bytes, _deduped_bytes);
|
||||
double deduped_old_percent = percent_of(_deduped_old, _deduped);
|
||||
double deduped_old_bytes_percent = percent_of(_deduped_old_bytes, _deduped_bytes);
|
||||
|
||||
log_debug(gc, stringdedup)(" Young: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")",
|
||||
_deduped_young, deduped_young_percent, STRDEDUP_BYTES_PARAM(_deduped_young_bytes), deduped_young_bytes_percent);
|
||||
log_debug(gc, stringdedup)(" Old: " STRDEDUP_OBJECTS_FORMAT "(" STRDEDUP_PERCENT_FORMAT ") " STRDEDUP_BYTES_FORMAT "(" STRDEDUP_PERCENT_FORMAT ")",
|
||||
_deduped_old, deduped_old_percent, STRDEDUP_BYTES_PARAM(_deduped_old_bytes), deduped_old_bytes_percent);
|
||||
|
||||
}
|
||||
|
||||
void G1StringDedupStat::reset() {
|
||||
StringDedupStat::reset();
|
||||
_deduped_young = 0;
|
||||
_deduped_young_bytes = 0;
|
||||
_deduped_old = 0;
|
||||
_deduped_old_bytes = 0;
|
||||
}
|
||||
|
@ -1,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"
|
||||
#include "gc/shared/stringdedup/stringDedupStat.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))
|
||||
|
||||
//
|
||||
// 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 deduped(oop obj, uintx bytes);
|
||||
|
||||
void inc_skipped() {
|
||||
_skipped++;
|
||||
}
|
||||
void add(const StringDedupStat* const stat);
|
||||
|
||||
void inc_hashed() {
|
||||
_hashed++;
|
||||
}
|
||||
void print_statistics(bool total) const;
|
||||
|
||||
void inc_known() {
|
||||
_known++;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
84
src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp
Normal file
84
src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp
Normal file
@ -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) {
|
||||
}
|
142
src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp
Normal file
142
src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp
Normal file
@ -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
|
@ -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
|
66
src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp
Normal file
66
src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.cpp
Normal file
@ -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");
|
||||
}
|
112
src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp
Normal file
112
src/hotspot/share/gc/shared/stringdedup/stringDedupQueue.hpp
Normal file
@ -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
|
@ -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
|
152
src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp
Normal file
152
src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp
Normal file
@ -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);
|
||||
}
|
139
src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp
Normal file
139
src/hotspot/share/gc/shared/stringdedup/stringDedupStat.hpp
Normal file
@ -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
|
||||
|
@ -25,12 +25,12 @@
|
||||
#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 "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"
|
||||
@ -41,25 +41,25 @@
|
||||
// List of deduplication table entries. Links table
|
||||
// entries together using their _next fields.
|
||||
//
|
||||
class G1StringDedupEntryList : public CHeapObj<mtGC> {
|
||||
class StringDedupEntryList : public CHeapObj<mtGC> {
|
||||
private:
|
||||
G1StringDedupEntry* _list;
|
||||
StringDedupEntry* _list;
|
||||
size_t _length;
|
||||
|
||||
public:
|
||||
G1StringDedupEntryList() :
|
||||
StringDedupEntryList() :
|
||||
_list(NULL),
|
||||
_length(0) {
|
||||
}
|
||||
|
||||
void add(G1StringDedupEntry* entry) {
|
||||
void add(StringDedupEntry* entry) {
|
||||
entry->set_next(_list);
|
||||
_list = entry;
|
||||
_length++;
|
||||
}
|
||||
|
||||
G1StringDedupEntry* remove() {
|
||||
G1StringDedupEntry* entry = _list;
|
||||
StringDedupEntry* remove() {
|
||||
StringDedupEntry* entry = _list;
|
||||
if (entry != NULL) {
|
||||
_list = entry->next();
|
||||
_length--;
|
||||
@ -67,8 +67,8 @@ public:
|
||||
return entry;
|
||||
}
|
||||
|
||||
G1StringDedupEntry* remove_all() {
|
||||
G1StringDedupEntry* list = _list;
|
||||
StringDedupEntry* remove_all() {
|
||||
StringDedupEntry* list = _list;
|
||||
_list = NULL;
|
||||
return list;
|
||||
}
|
||||
@ -92,28 +92,28 @@ public:
|
||||
// Allocations are synchronized by StringDedupTable_lock as part of a table
|
||||
// modification.
|
||||
//
|
||||
class G1StringDedupEntryCache : public CHeapObj<mtGC> {
|
||||
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<G1StringDedupEntryList>* _cached;
|
||||
PaddedEnd<G1StringDedupEntryList>* _overflowed;
|
||||
PaddedEnd<StringDedupEntryList>* _cached;
|
||||
PaddedEnd<StringDedupEntryList>* _overflowed;
|
||||
|
||||
public:
|
||||
G1StringDedupEntryCache(size_t max_size);
|
||||
~G1StringDedupEntryCache();
|
||||
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.
|
||||
G1StringDedupEntry* alloc();
|
||||
StringDedupEntry* alloc();
|
||||
|
||||
// Insert a table entry into the cache.
|
||||
void free(G1StringDedupEntry* entry, uint worker_id);
|
||||
void free(StringDedupEntry* entry, uint worker_id);
|
||||
|
||||
// Returns current number of entries in the cache.
|
||||
size_t size();
|
||||
@ -122,33 +122,33 @@ public:
|
||||
void delete_overflowed();
|
||||
};
|
||||
|
||||
G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) :
|
||||
StringDedupEntryCache::StringDedupEntryCache(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)) {
|
||||
_cached(PaddedArray<StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)),
|
||||
_overflowed(PaddedArray<StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)) {
|
||||
set_max_size(max_size);
|
||||
}
|
||||
|
||||
G1StringDedupEntryCache::~G1StringDedupEntryCache() {
|
||||
StringDedupEntryCache::~StringDedupEntryCache() {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
void G1StringDedupEntryCache::set_max_size(size_t size) {
|
||||
void StringDedupEntryCache::set_max_size(size_t size) {
|
||||
_max_list_length = size / _nlists;
|
||||
}
|
||||
|
||||
G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
|
||||
StringDedupEntry* StringDedupEntryCache::alloc() {
|
||||
for (size_t i = 0; i < _nlists; i++) {
|
||||
G1StringDedupEntry* entry = _cached[i].remove();
|
||||
StringDedupEntry* entry = _cached[i].remove();
|
||||
if (entry != NULL) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return new G1StringDedupEntry();
|
||||
return new StringDedupEntry();
|
||||
}
|
||||
|
||||
void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
|
||||
void StringDedupEntryCache::free(StringDedupEntry* entry, uint worker_id) {
|
||||
assert(entry->obj() != NULL, "Double free");
|
||||
assert(worker_id < _nlists, "Invalid worker id");
|
||||
|
||||
@ -164,7 +164,7 @@ void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
|
||||
}
|
||||
}
|
||||
|
||||
size_t G1StringDedupEntryCache::size() {
|
||||
size_t StringDedupEntryCache::size() {
|
||||
size_t size = 0;
|
||||
for (size_t i = 0; i < _nlists; i++) {
|
||||
size += _cached[i].length();
|
||||
@ -172,12 +172,12 @@ size_t G1StringDedupEntryCache::size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
void G1StringDedupEntryCache::delete_overflowed() {
|
||||
void StringDedupEntryCache::delete_overflowed() {
|
||||
double start = os::elapsedTime();
|
||||
uintx count = 0;
|
||||
|
||||
for (size_t i = 0; i < _nlists; i++) {
|
||||
G1StringDedupEntry* entry;
|
||||
StringDedupEntry* entry;
|
||||
|
||||
{
|
||||
// The overflow list can be modified during safepoints, therefore
|
||||
@ -189,7 +189,7 @@ void G1StringDedupEntryCache::delete_overflowed() {
|
||||
|
||||
// Delete all entries
|
||||
while (entry != NULL) {
|
||||
G1StringDedupEntry* next = entry->next();
|
||||
StringDedupEntry* next = entry->next();
|
||||
delete entry;
|
||||
entry = next;
|
||||
count++;
|
||||
@ -197,27 +197,31 @@ void G1StringDedupEntryCache::delete_overflowed() {
|
||||
}
|
||||
|
||||
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));
|
||||
log_trace(gc, stringdedup)("Deleted " UINTX_FORMAT " entries, " STRDEDUP_TIME_FORMAT_MS,
|
||||
count, STRDEDUP_TIME_PARAM_MS(end - start));
|
||||
}
|
||||
|
||||
G1StringDedupTable* G1StringDedupTable::_table = NULL;
|
||||
G1StringDedupEntryCache* G1StringDedupTable::_entry_cache = NULL;
|
||||
StringDedupTable* StringDedupTable::_table = NULL;
|
||||
StringDedupEntryCache* StringDedupTable::_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);
|
||||
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 G1StringDedupTable::_entries_added = 0;
|
||||
uintx G1StringDedupTable::_entries_removed = 0;
|
||||
uintx G1StringDedupTable::_resize_count = 0;
|
||||
uintx G1StringDedupTable::_rehash_count = 0;
|
||||
uintx StringDedupTable::_entries_added = 0;
|
||||
uintx StringDedupTable::_entries_removed = 0;
|
||||
uintx StringDedupTable::_resize_count = 0;
|
||||
uintx StringDedupTable::_rehash_count = 0;
|
||||
|
||||
G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) :
|
||||
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)),
|
||||
@ -225,22 +229,22 @@ G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) :
|
||||
_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*));
|
||||
_buckets = NEW_C_HEAP_ARRAY(StringDedupEntry*, _size, mtGC);
|
||||
memset(_buckets, 0, _size * sizeof(StringDedupEntry*));
|
||||
}
|
||||
|
||||
G1StringDedupTable::~G1StringDedupTable() {
|
||||
StringDedupTable::~StringDedupTable() {
|
||||
FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets);
|
||||
}
|
||||
|
||||
void G1StringDedupTable::create() {
|
||||
void StringDedupTable::create() {
|
||||
assert(_table == NULL, "One string deduplication table allowed");
|
||||
_entry_cache = new G1StringDedupEntryCache(_min_size * _max_cache_factor);
|
||||
_table = new G1StringDedupTable(_min_size);
|
||||
_entry_cache = new StringDedupEntryCache(_min_size * _max_cache_factor);
|
||||
_table = new StringDedupTable(_min_size);
|
||||
}
|
||||
|
||||
void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list) {
|
||||
G1StringDedupEntry* entry = _entry_cache->alloc();
|
||||
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);
|
||||
@ -249,38 +253,41 @@ void G1StringDedupTable::add(typeArrayOop value, bool latin1, unsigned int hash,
|
||||
_entries++;
|
||||
}
|
||||
|
||||
void G1StringDedupTable::remove(G1StringDedupEntry** pentry, uint worker_id) {
|
||||
G1StringDedupEntry* entry = *pentry;
|
||||
void StringDedupTable::remove(StringDedupEntry** pentry, uint worker_id) {
|
||||
StringDedupEntry* entry = *pentry;
|
||||
*pentry = entry->next();
|
||||
_entry_cache->free(entry, worker_id);
|
||||
}
|
||||
|
||||
void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest) {
|
||||
G1StringDedupEntry* entry = *pentry;
|
||||
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);
|
||||
G1StringDedupEntry** list = dest->bucket(index);
|
||||
StringDedupEntry** list = dest->bucket(index);
|
||||
entry->set_next(*list);
|
||||
*list = entry;
|
||||
}
|
||||
|
||||
bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) {
|
||||
return (value1 == value2 ||
|
||||
bool StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) {
|
||||
return (oopDesc::equals(value1, value2) ||
|
||||
(value1->length() == value2->length() &&
|
||||
(!memcmp(value1->base(T_BYTE),
|
||||
(!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()) {
|
||||
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)) {
|
||||
// Match found
|
||||
return 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++;
|
||||
@ -290,9 +297,9 @@ typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, bool latin1, unsigne
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) {
|
||||
typeArrayOop StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool latin1, unsigned int hash) {
|
||||
size_t index = hash_to_index(hash);
|
||||
G1StringDedupEntry** list = bucket(index);
|
||||
StringDedupEntry** list = bucket(index);
|
||||
uintx count = 0;
|
||||
|
||||
// Lookup in list
|
||||
@ -314,7 +321,7 @@ typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, bool la
|
||||
return existing_value;
|
||||
}
|
||||
|
||||
unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) {
|
||||
unsigned int StringDedupTable::hash_code(typeArrayOop value, bool latin1) {
|
||||
unsigned int hash;
|
||||
int length = value->length();
|
||||
if (latin1) {
|
||||
@ -337,16 +344,16 @@ unsigned int G1StringDedupTable::hash_code(typeArrayOop value, bool latin1) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
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();
|
||||
stat->inc_inspected();
|
||||
|
||||
typeArrayOop value = java_lang_String::value(java_string);
|
||||
if (value == NULL) {
|
||||
// String has no value
|
||||
stat.inc_skipped();
|
||||
stat->inc_skipped();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -361,7 +368,7 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
if (hash == 0) {
|
||||
// Compute hash
|
||||
hash = hash_code(value, latin1);
|
||||
stat.inc_hashed();
|
||||
stat->inc_hashed();
|
||||
|
||||
if (use_java_hash() && hash != 0) {
|
||||
// Store hash code in cache
|
||||
@ -372,31 +379,30 @@ void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) {
|
||||
typeArrayOop existing_value = lookup_or_add(value, latin1, hash);
|
||||
if (existing_value == value) {
|
||||
// Same value, already known
|
||||
stat.inc_known();
|
||||
stat->inc_known();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get size of value array
|
||||
uintx size_in_bytes = value->size() * HeapWordSize;
|
||||
stat.inc_new(size_in_bytes);
|
||||
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);
|
||||
}
|
||||
stat->deduped(value, size_in_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
G1StringDedupTable* G1StringDedupTable::prepare_resize() {
|
||||
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
|
||||
@ -434,10 +440,10 @@ G1StringDedupTable* G1StringDedupTable::prepare_resize() {
|
||||
|
||||
// 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);
|
||||
return new StringDedupTable(size, _table->_hash_seed);
|
||||
}
|
||||
|
||||
void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) {
|
||||
void StringDedupTable::finish_resize(StringDedupTable* resized_table) {
|
||||
assert(resized_table != NULL, "Invalid table");
|
||||
|
||||
resized_table->_entries = _table->_entries;
|
||||
@ -449,7 +455,7 @@ void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) {
|
||||
_table = resized_table;
|
||||
}
|
||||
|
||||
void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) {
|
||||
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
|
||||
@ -464,7 +470,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
|
||||
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*));
|
||||
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
|
||||
@ -472,7 +478,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
|
||||
|
||||
for (;;) {
|
||||
// Grab next partition to scan
|
||||
size_t partition_begin = cl->claim_table_partition(partition_size);
|
||||
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
|
||||
@ -492,22 +498,22 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
|
||||
}
|
||||
}
|
||||
|
||||
uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
|
||||
size_t partition_begin,
|
||||
size_t partition_end,
|
||||
uint worker_id) {
|
||||
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++) {
|
||||
G1StringDedupEntry** entry = _table->bucket(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 (cl->is_resizing()) {
|
||||
if (is_resizing()) {
|
||||
// We are resizing the table, transfer entry to the new table
|
||||
_table->transfer(entry, cl->resized_table());
|
||||
_table->transfer(entry, _resized_table);
|
||||
} else {
|
||||
if (cl->is_rehashing()) {
|
||||
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
|
||||
@ -533,7 +539,34 @@ uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure*
|
||||
return removed;
|
||||
}
|
||||
|
||||
G1StringDedupTable* G1StringDedupTable::prepare_rehash() {
|
||||
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;
|
||||
@ -546,15 +579,15 @@ G1StringDedupTable* G1StringDedupTable::prepare_rehash() {
|
||||
_table->_hash_seed = AltHashing::compute_seed();
|
||||
|
||||
// Allocate the new table, same size and hash seed
|
||||
return new G1StringDedupTable(_table->_size, _table->_hash_seed);
|
||||
return new StringDedupTable(_table->_size, _table->_hash_seed);
|
||||
}
|
||||
|
||||
void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) {
|
||||
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++) {
|
||||
G1StringDedupEntry** entry = _table->bucket(bucket);
|
||||
StringDedupEntry** entry = _table->bucket(bucket);
|
||||
while (*entry != NULL) {
|
||||
_table->transfer(entry, rehashed_table);
|
||||
}
|
||||
@ -569,14 +602,18 @@ void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) {
|
||||
_table = rehashed_table;
|
||||
}
|
||||
|
||||
void G1StringDedupTable::verify() {
|
||||
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
|
||||
G1StringDedupEntry** entry = _table->bucket(bucket);
|
||||
StringDedupEntry** 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(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();
|
||||
@ -590,11 +627,11 @@ void G1StringDedupTable::verify() {
|
||||
// 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);
|
||||
StringDedupEntry** entry1 = _table->bucket(bucket);
|
||||
while (*entry1 != NULL) {
|
||||
typeArrayOop value1 = (*entry1)->obj();
|
||||
bool latin1_1 = (*entry1)->latin1();
|
||||
G1StringDedupEntry** entry2 = (*entry1)->next_addr();
|
||||
StringDedupEntry** entry2 = (*entry1)->next_addr();
|
||||
while (*entry2 != NULL) {
|
||||
typeArrayOop value2 = (*entry2)->obj();
|
||||
bool latin1_2 = (*entry2)->latin1();
|
||||
@ -606,19 +643,19 @@ void G1StringDedupTable::verify() {
|
||||
}
|
||||
}
|
||||
|
||||
void G1StringDedupTable::clean_entry_cache() {
|
||||
void StringDedupTable::clean_entry_cache() {
|
||||
_entry_cache->delete_overflowed();
|
||||
}
|
||||
|
||||
void G1StringDedupTable::print_statistics() {
|
||||
void StringDedupTable::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(" 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: " G1_STRDEDUP_PERCENT_FORMAT_NS ", Cached: " UINTX_FORMAT ", Added: " UINTX_FORMAT ", Removed: " UINTX_FORMAT,
|
||||
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 "(" G1_STRDEDUP_PERCENT_FORMAT_NS "), Grow Threshold: " UINTX_FORMAT "(" G1_STRDEDUP_PERCENT_FORMAT_NS ")",
|
||||
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);
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -22,44 +22,44 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
|
||||
#define SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
|
||||
#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
|
||||
#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
|
||||
|
||||
#include "gc/g1/g1StringDedupStat.hpp"
|
||||
#include "gc/shared/stringdedup/stringDedupStat.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
|
||||
class G1StringDedupEntryCache;
|
||||
class G1StringDedupUnlinkOrOopsDoClosure;
|
||||
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 G1StringDedupEntry : public CHeapObj<mtGC> {
|
||||
class StringDedupEntry : public CHeapObj<mtGC> {
|
||||
private:
|
||||
G1StringDedupEntry* _next;
|
||||
StringDedupEntry* _next;
|
||||
unsigned int _hash;
|
||||
bool _latin1;
|
||||
typeArrayOop _obj;
|
||||
|
||||
public:
|
||||
G1StringDedupEntry() :
|
||||
StringDedupEntry() :
|
||||
_next(NULL),
|
||||
_hash(0),
|
||||
_latin1(false),
|
||||
_obj(NULL) {
|
||||
}
|
||||
|
||||
G1StringDedupEntry* next() {
|
||||
StringDedupEntry* next() {
|
||||
return _next;
|
||||
}
|
||||
|
||||
G1StringDedupEntry** next_addr() {
|
||||
StringDedupEntry** next_addr() {
|
||||
return &_next;
|
||||
}
|
||||
|
||||
void set_next(G1StringDedupEntry* next) {
|
||||
void set_next(StringDedupEntry* next) {
|
||||
_next = next;
|
||||
}
|
||||
|
||||
@ -111,16 +111,16 @@ public:
|
||||
// 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> {
|
||||
class StringDedupTable : public CHeapObj<mtGC> {
|
||||
private:
|
||||
// The currently active hashtable instance. Only modified when
|
||||
// the table is resizes or rehashed.
|
||||
static G1StringDedupTable* _table;
|
||||
static StringDedupTable* _table;
|
||||
|
||||
// Cache for reuse and fast alloc/free of table entries.
|
||||
static G1StringDedupEntryCache* _entry_cache;
|
||||
static StringDedupEntryCache* _entry_cache;
|
||||
|
||||
G1StringDedupEntry** _buckets;
|
||||
StringDedupEntry** _buckets;
|
||||
size_t _size;
|
||||
uintx _entries;
|
||||
uintx _shrink_threshold;
|
||||
@ -148,11 +148,16 @@ private:
|
||||
static uintx _resize_count;
|
||||
static uintx _rehash_count;
|
||||
|
||||
G1StringDedupTable(size_t size, jint hash_seed = 0);
|
||||
~G1StringDedupTable();
|
||||
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.
|
||||
G1StringDedupEntry** bucket(size_t index) {
|
||||
StringDedupEntry** bucket(size_t index) {
|
||||
return _buckets + index;
|
||||
}
|
||||
|
||||
@ -162,18 +167,18 @@ private:
|
||||
}
|
||||
|
||||
// Adds a new table entry to the given hash bucket.
|
||||
void add(typeArrayOop value, bool latin1, unsigned int hash, G1StringDedupEntry** list);
|
||||
void add(typeArrayOop value, bool latin1, unsigned int hash, StringDedupEntry** list);
|
||||
|
||||
// Removes the given table entry from the table.
|
||||
void remove(G1StringDedupEntry** pentry, uint worker_id);
|
||||
void remove(StringDedupEntry** pentry, uint worker_id);
|
||||
|
||||
// Transfers a table entry from the current table to the destination table.
|
||||
void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest);
|
||||
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,
|
||||
G1StringDedupEntry** list, uintx &count);
|
||||
StringDedupEntry** list, uintx &count);
|
||||
|
||||
// Returns an existing character array in the table, or inserts a new
|
||||
// table entry if no matching character array exists.
|
||||
@ -200,42 +205,51 @@ private:
|
||||
// currently active hash function and hash seed.
|
||||
static unsigned int hash_code(typeArrayOop value, bool latin1);
|
||||
|
||||
static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl,
|
||||
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, G1StringDedupStat& stat);
|
||||
static void deduplicate(oop java_string, StringDedupStat* stat);
|
||||
|
||||
// If a table resize is needed, returns a newly allocated empty
|
||||
// hashtable of the proper size.
|
||||
static G1StringDedupTable* prepare_resize();
|
||||
static void unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
|
||||
|
||||
// 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);
|
||||
static void print_statistics();
|
||||
static void verify();
|
||||
|
||||
// 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();
|
||||
// GC support
|
||||
static void gc_prologue(bool resize_and_rehash_table);
|
||||
static void gc_epilogue();
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTABLE_HPP
|
||||
#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -22,11 +22,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
|
||||
#define SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
|
||||
#ifndef SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP
|
||||
#define SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP
|
||||
|
||||
#include "gc/g1/g1StringDedupStat.hpp"
|
||||
#include "gc/shared/concurrentGCThread.hpp"
|
||||
#include "gc/shared/stringdedup/stringDedupStat.hpp"
|
||||
|
||||
//
|
||||
// The deduplication thread is where the actual deduplication occurs. It waits for
|
||||
@ -36,25 +36,37 @@
|
||||
// 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;
|
||||
class StringDedupThread: public ConcurrentGCThread {
|
||||
protected:
|
||||
static StringDedupThread* _thread;
|
||||
|
||||
G1StringDedupThread();
|
||||
~G1StringDedupThread();
|
||||
StringDedupThread();
|
||||
~StringDedupThread();
|
||||
|
||||
void print_start(const G1StringDedupStat& last_stat);
|
||||
void print_end(const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat);
|
||||
void print_start(const StringDedupStat* last_stat);
|
||||
void print_end(const StringDedupStat* last_stat, const StringDedupStat* total_stat);
|
||||
|
||||
void run_service();
|
||||
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();
|
||||
|
||||
static G1StringDedupThread* thread();
|
||||
|
||||
void deduplicate_shared_strings(G1StringDedupStat& stat);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
|
||||
#endif // SHARE_VM_GC_SHARED_STRINGDEDUP_STRINGDEDUPTHREAD_HPP
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user