From 16670578399d9184a5aaabc63217f9fa0e679705 Mon Sep 17 00:00:00 2001 From: vkempik Date: Thu, 30 Jun 2016 17:28:39 +0300 Subject: [PATCH] 8158871: Long response times with G1 and StringDeduplication Reviewed-by: pliden, tschatzl --- .../g1/g1StringDedupTable.cpp | 114 ++++++++++++------ .../g1/g1StringDedupTable.hpp | 6 +- .../g1/g1StringDedupThread.cpp | 4 +- 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp b/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp index 2b41688a3..71e7e539c 100644 --- a/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp @@ -1,5 +1,5 @@ /* - * 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. * * This code is free software; you can redistribute it and/or modify it @@ -28,22 +28,23 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/g1StringDedupTable.hpp" +#include "gc_implementation/shared/concurrentGCThread.hpp" #include "memory/gcLocker.hpp" #include "memory/padded.inline.hpp" #include "oops/typeArrayOop.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. // -class G1StringDedupEntryFreeList : public CHeapObj { +class G1StringDedupEntryList : public CHeapObj { private: G1StringDedupEntry* _list; size_t _length; public: - G1StringDedupEntryFreeList() : + G1StringDedupEntryList() : _list(NULL), _length(0) { } @@ -63,6 +64,12 @@ public: return entry; } + G1StringDedupEntry* remove_all() { + G1StringDedupEntry* list = _list; + _list = NULL; + return list; + } + size_t length() { return _length; } @@ -84,43 +91,53 @@ public: // class G1StringDedupEntryCache : public CHeapObj { private: - // One freelist per GC worker to allow lock less freeing of - // entries while doing a parallel scan of the table. Using - // PaddedEnd to avoid false sharing. - PaddedEnd* _lists; - size_t _nlists; + // 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* _cached; + PaddedEnd* _overflowed; public: - G1StringDedupEntryCache(); + G1StringDedupEntryCache(size_t max_size); ~G1StringDedupEntryCache(); - // Get a table entry from the cache freelist, or allocate a new - // entry if the cache is empty. + // Set max number of table entries to cache. + void set_max_size(size_t max_size); + + // Get a table entry from the cache, or allocate a new entry if the cache is empty. G1StringDedupEntry* alloc(); - // Insert a table entry into the cache freelist. + // Insert a table entry into the cache. void free(G1StringDedupEntry* entry, uint worker_id); // Returns current number of entries in the cache. size_t size(); - // If the cache has grown above the given max size, trim it down - // and deallocate the memory occupied by trimmed of entries. - void trim(size_t max_size); + // Deletes overflowed entries. + void delete_overflowed(); }; -G1StringDedupEntryCache::G1StringDedupEntryCache() { - _nlists = MAX2(ParallelGCThreads, (size_t)1); - _lists = PaddedArray::create_unfreeable((uint)_nlists); +G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) : + _nlists(MAX2(ParallelGCThreads, (size_t)1)), + _max_list_length(0), + _cached(PaddedArray::create_unfreeable((uint)_nlists)), + _overflowed(PaddedArray::create_unfreeable((uint)_nlists)) { + set_max_size(max_size); } G1StringDedupEntryCache::~G1StringDedupEntryCache() { ShouldNotReachHere(); } +void G1StringDedupEntryCache::set_max_size(size_t size) { + _max_list_length = size / _nlists; +} + G1StringDedupEntry* G1StringDedupEntryCache::alloc() { for (size_t i = 0; i < _nlists; i++) { - G1StringDedupEntry* entry = _lists[i].remove(); + G1StringDedupEntry* entry = _cached[i].remove(); if (entry != NULL) { return entry; } @@ -131,31 +148,55 @@ G1StringDedupEntry* G1StringDedupEntryCache::alloc() { void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) { assert(entry->obj() != NULL, "Double free"); assert(worker_id < _nlists, "Invalid worker id"); + entry->set_obj(NULL); entry->set_hash(0); - _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 size = 0; for (size_t i = 0; i < _nlists; i++) { - size += _lists[i].length(); + size += _cached[i].length(); } return size; } -void G1StringDedupEntryCache::trim(size_t max_size) { - size_t cache_size = 0; +void G1StringDedupEntryCache::delete_overflowed() { + double start = os::elapsedTime(); + uintx count = 0; + for (size_t i = 0; i < _nlists; i++) { - G1StringDedupEntryFreeList* list = &_lists[i]; - cache_size += list->length(); - while (cache_size > max_size) { - G1StringDedupEntry* entry = list->remove(); - assert(entry != NULL, "Should not be null"); - cache_size--; + G1StringDedupEntry* entry; + + { + // The overflow list can be modified during safepoints, therefore + // we temporarily join the suspendible thread set while removing + // all entries from the list. + SuspendibleThreadSetJoiner sts_join; + entry = _overflowed[i].remove_all(); + } + + // Delete all entries + while (entry != NULL) { + G1StringDedupEntry* next = entry->next(); delete entry; + entry = next; + count++; } } + + double end = os::elapsedTime(); + 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; @@ -192,7 +233,7 @@ G1StringDedupTable::~G1StringDedupTable() { void G1StringDedupTable::create() { 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); } @@ -375,6 +416,9 @@ G1StringDedupTable* G1StringDedupTable::prepare_resize() { // Update statistics _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 // calling unlink_or_oops_do() and finally installed by finish_resize(). return new G1StringDedupTable(size, _table->_hash_seed); @@ -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); } - // Delayed update avoid contention on the table lock + // Delayed update to avoid contention on the table lock if (removed > 0) { MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); _table->_entries -= removed; @@ -545,10 +589,8 @@ void G1StringDedupTable::verify() { } } -void G1StringDedupTable::trim_entry_cache() { - MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); - size_t max_cache_size = (size_t)(_table->_size * _max_cache_factor); - _entry_cache->trim(max_cache_size); +void G1StringDedupTable::clean_entry_cache() { + _entry_cache->delete_overflowed(); } void G1StringDedupTable::print_statistics(outputStream* st) { diff --git a/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp b/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp index f357523c5..d7712195a 100644 --- a/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp @@ -1,5 +1,5 @@ /* - * 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. * * This code is free software; you can redistribute it and/or modify it @@ -218,8 +218,8 @@ public: // and deletes the previously active table. static void finish_rehash(G1StringDedupTable* rehashed_table); - // If the table entry cache has grown too large, trim it down according to policy - static void trim_entry_cache(); + // 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); diff --git a/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp b/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp index e59efa7b8..09c567e2e 100644 --- a/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp +++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp @@ -100,14 +100,14 @@ void G1StringDedupThread::run() { } } - G1StringDedupTable::trim_entry_cache(); - stat.mark_done(); // Print statistics total_stat.add(stat); print(gclog_or_tty, stat, total_stat); } + + G1StringDedupTable::clean_entry_cache(); } terminate(); -- GitLab