提交 16670578 编写于 作者: V vkempik

8158871: Long response times with G1 and StringDeduplication

Reviewed-by: pliden, tschatzl
上级 e1b27304
/* /*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -28,22 +28,23 @@ ...@@ -28,22 +28,23 @@
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
#include "gc_implementation/g1/g1StringDedupTable.hpp" #include "gc_implementation/g1/g1StringDedupTable.hpp"
#include "gc_implementation/shared/concurrentGCThread.hpp"
#include "memory/gcLocker.hpp" #include "memory/gcLocker.hpp"
#include "memory/padded.inline.hpp" #include "memory/padded.inline.hpp"
#include "oops/typeArrayOop.hpp" #include "oops/typeArrayOop.hpp"
#include "runtime/mutexLocker.hpp" #include "runtime/mutexLocker.hpp"
// //
// Freelist in the deduplication table entry cache. Links table // List of deduplication table entries. Links table
// entries together using their _next fields. // entries together using their _next fields.
// //
class G1StringDedupEntryFreeList : public CHeapObj<mtGC> { class G1StringDedupEntryList : public CHeapObj<mtGC> {
private: private:
G1StringDedupEntry* _list; G1StringDedupEntry* _list;
size_t _length; size_t _length;
public: public:
G1StringDedupEntryFreeList() : G1StringDedupEntryList() :
_list(NULL), _list(NULL),
_length(0) { _length(0) {
} }
...@@ -63,6 +64,12 @@ public: ...@@ -63,6 +64,12 @@ public:
return entry; return entry;
} }
G1StringDedupEntry* remove_all() {
G1StringDedupEntry* list = _list;
_list = NULL;
return list;
}
size_t length() { size_t length() {
return _length; return _length;
} }
...@@ -84,43 +91,53 @@ public: ...@@ -84,43 +91,53 @@ public:
// //
class G1StringDedupEntryCache : public CHeapObj<mtGC> { class G1StringDedupEntryCache : public CHeapObj<mtGC> {
private: private:
// One freelist per GC worker to allow lock less freeing of // One cache/overflow list per GC worker to allow lock less freeing of
// entries while doing a parallel scan of the table. Using // entries while doing a parallel scan of the table. Using PaddedEnd to
// PaddedEnd to avoid false sharing. // avoid false sharing.
PaddedEnd<G1StringDedupEntryFreeList>* _lists; size_t _nlists;
size_t _nlists; size_t _max_list_length;
PaddedEnd<G1StringDedupEntryList>* _cached;
PaddedEnd<G1StringDedupEntryList>* _overflowed;
public: public:
G1StringDedupEntryCache(); G1StringDedupEntryCache(size_t max_size);
~G1StringDedupEntryCache(); ~G1StringDedupEntryCache();
// Get a table entry from the cache freelist, or allocate a new // Set max number of table entries to cache.
// entry if the cache is empty. 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(); G1StringDedupEntry* alloc();
// Insert a table entry into the cache freelist. // Insert a table entry into the cache.
void free(G1StringDedupEntry* entry, uint worker_id); void free(G1StringDedupEntry* entry, uint worker_id);
// Returns current number of entries in the cache. // Returns current number of entries in the cache.
size_t size(); size_t size();
// If the cache has grown above the given max size, trim it down // Deletes overflowed entries.
// and deallocate the memory occupied by trimmed of entries. void delete_overflowed();
void trim(size_t max_size);
}; };
G1StringDedupEntryCache::G1StringDedupEntryCache() { G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) :
_nlists = MAX2(ParallelGCThreads, (size_t)1); _nlists(MAX2(ParallelGCThreads, (size_t)1)),
_lists = PaddedArray<G1StringDedupEntryFreeList, mtGC>::create_unfreeable((uint)_nlists); _max_list_length(0),
_cached(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)),
_overflowed(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)) {
set_max_size(max_size);
} }
G1StringDedupEntryCache::~G1StringDedupEntryCache() { G1StringDedupEntryCache::~G1StringDedupEntryCache() {
ShouldNotReachHere(); ShouldNotReachHere();
} }
void G1StringDedupEntryCache::set_max_size(size_t size) {
_max_list_length = size / _nlists;
}
G1StringDedupEntry* G1StringDedupEntryCache::alloc() { G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
for (size_t i = 0; i < _nlists; i++) { for (size_t i = 0; i < _nlists; i++) {
G1StringDedupEntry* entry = _lists[i].remove(); G1StringDedupEntry* entry = _cached[i].remove();
if (entry != NULL) { if (entry != NULL) {
return entry; return entry;
} }
...@@ -131,31 +148,55 @@ G1StringDedupEntry* G1StringDedupEntryCache::alloc() { ...@@ -131,31 +148,55 @@ G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) { void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
assert(entry->obj() != NULL, "Double free"); assert(entry->obj() != NULL, "Double free");
assert(worker_id < _nlists, "Invalid worker id"); assert(worker_id < _nlists, "Invalid worker id");
entry->set_obj(NULL); entry->set_obj(NULL);
entry->set_hash(0); entry->set_hash(0);
_lists[worker_id].add(entry);
if (_cached[worker_id].length() < _max_list_length) {
// Cache is not full
_cached[worker_id].add(entry);
} else {
// Cache is full, add to overflow list for later deletion
_overflowed[worker_id].add(entry);
}
} }
size_t G1StringDedupEntryCache::size() { size_t G1StringDedupEntryCache::size() {
size_t size = 0; size_t size = 0;
for (size_t i = 0; i < _nlists; i++) { for (size_t i = 0; i < _nlists; i++) {
size += _lists[i].length(); size += _cached[i].length();
} }
return size; return size;
} }
void G1StringDedupEntryCache::trim(size_t max_size) { void G1StringDedupEntryCache::delete_overflowed() {
size_t cache_size = 0; double start = os::elapsedTime();
uintx count = 0;
for (size_t i = 0; i < _nlists; i++) { for (size_t i = 0; i < _nlists; i++) {
G1StringDedupEntryFreeList* list = &_lists[i]; G1StringDedupEntry* entry;
cache_size += list->length();
while (cache_size > max_size) { {
G1StringDedupEntry* entry = list->remove(); // The overflow list can be modified during safepoints, therefore
assert(entry != NULL, "Should not be null"); // we temporarily join the suspendible thread set while removing
cache_size--; // all entries from the list.
SuspendibleThreadSetJoiner sts_join;
entry = _overflowed[i].remove_all();
}
// Delete all entries
while (entry != NULL) {
G1StringDedupEntry* next = entry->next();
delete entry; delete entry;
entry = next;
count++;
} }
} }
double end = os::elapsedTime();
if (PrintStringDeduplicationStatistics) {
gclog_or_tty->print_cr("[GC concurrent-string-deduplication, deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT "]", count, end - start);
}
} }
G1StringDedupTable* G1StringDedupTable::_table = NULL; G1StringDedupTable* G1StringDedupTable::_table = NULL;
...@@ -192,7 +233,7 @@ G1StringDedupTable::~G1StringDedupTable() { ...@@ -192,7 +233,7 @@ G1StringDedupTable::~G1StringDedupTable() {
void G1StringDedupTable::create() { void G1StringDedupTable::create() {
assert(_table == NULL, "One string deduplication table allowed"); assert(_table == NULL, "One string deduplication table allowed");
_entry_cache = new G1StringDedupEntryCache(); _entry_cache = new G1StringDedupEntryCache((size_t)(_min_size * _max_cache_factor));
_table = new G1StringDedupTable(_min_size); _table = new G1StringDedupTable(_min_size);
} }
...@@ -375,6 +416,9 @@ G1StringDedupTable* G1StringDedupTable::prepare_resize() { ...@@ -375,6 +416,9 @@ G1StringDedupTable* G1StringDedupTable::prepare_resize() {
// Update statistics // Update statistics
_resize_count++; _resize_count++;
// Update max cache size
_entry_cache->set_max_size((size_t)(size * _max_cache_factor));
// Allocate the new table. The new table will be populated by workers // Allocate the new table. The new table will be populated by workers
// calling unlink_or_oops_do() and finally installed by finish_resize(). // calling unlink_or_oops_do() and finally installed by finish_resize().
return new G1StringDedupTable(size, _table->_hash_seed); return new G1StringDedupTable(size, _table->_hash_seed);
...@@ -427,7 +471,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c ...@@ -427,7 +471,7 @@ void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* c
removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id); removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id);
} }
// Delayed update avoid contention on the table lock // Delayed update to avoid contention on the table lock
if (removed > 0) { if (removed > 0) {
MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
_table->_entries -= removed; _table->_entries -= removed;
...@@ -545,10 +589,8 @@ void G1StringDedupTable::verify() { ...@@ -545,10 +589,8 @@ void G1StringDedupTable::verify() {
} }
} }
void G1StringDedupTable::trim_entry_cache() { void G1StringDedupTable::clean_entry_cache() {
MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); _entry_cache->delete_overflowed();
size_t max_cache_size = (size_t)(_table->_size * _max_cache_factor);
_entry_cache->trim(max_cache_size);
} }
void G1StringDedupTable::print_statistics(outputStream* st) { void G1StringDedupTable::print_statistics(outputStream* st) {
......
/* /*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -218,8 +218,8 @@ public: ...@@ -218,8 +218,8 @@ public:
// and deletes the previously active table. // and deletes the previously active table.
static void finish_rehash(G1StringDedupTable* rehashed_table); static void finish_rehash(G1StringDedupTable* rehashed_table);
// If the table entry cache has grown too large, trim it down according to policy // If the table entry cache has grown too large, delete overflowed entries.
static void trim_entry_cache(); static void clean_entry_cache();
static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id); static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
......
...@@ -100,14 +100,14 @@ void G1StringDedupThread::run() { ...@@ -100,14 +100,14 @@ void G1StringDedupThread::run() {
} }
} }
G1StringDedupTable::trim_entry_cache();
stat.mark_done(); stat.mark_done();
// Print statistics // Print statistics
total_stat.add(stat); total_stat.add(stat);
print(gclog_or_tty, stat, total_stat); print(gclog_or_tty, stat, total_stat);
} }
G1StringDedupTable::clean_entry_cache();
} }
terminate(); terminate();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册