# HG changeset patch # User zgu # Date 1534897797 14400 # Node ID 6c0ac4b4d761dcfdb25295623517634bb2314ab7 # Parent 492b366f8e5784cc4927c2c98f9b8a3f16c067eb 8206467: Refactor G1ParallelCleaningTask into shared Summary: Refactored and moved G1ParllelCleaningTask to be shared among GCs Reviewed-by: rkennke, kbarrett diff -r 492b366f8e57 -r 6c0ac4b4d761 src/hotspot/share/gc/g1/g1CollectedHeap.cpp --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Tue Aug 21 13:17:29 2018 -0700 +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Tue Aug 21 20:29:57 2018 -0400 @@ -70,6 +70,7 @@ #include "gc/shared/generationSpec.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/oopStorageParState.hpp" +#include "gc/shared/parallelCleaning.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/referenceProcessor.inline.hpp" @@ -3254,334 +3255,12 @@ undo_waste * HeapWordSize / K); } -class G1StringCleaningTask : public AbstractGangTask { -private: - BoolObjectClosure* _is_alive; - G1StringDedupUnlinkOrOopsDoClosure _dedup_closure; - OopStorage::ParState _par_state_string; - - int _initial_string_table_size; - - bool _process_strings; - int _strings_processed; - int _strings_removed; - - bool _process_string_dedup; - -public: - G1StringCleaningTask(BoolObjectClosure* is_alive, bool process_strings, bool process_string_dedup) : - AbstractGangTask("String Unlinking"), - _is_alive(is_alive), - _dedup_closure(is_alive, NULL, false), - _par_state_string(StringTable::weak_storage()), - _process_strings(process_strings), _strings_processed(0), _strings_removed(0), - _process_string_dedup(process_string_dedup) { - - _initial_string_table_size = (int) StringTable::the_table()->table_size(); - if (process_strings) { - StringTable::reset_dead_counter(); - } - } - - ~G1StringCleaningTask() { - log_info(gc, stringtable)( - "Cleaned string table, " - "strings: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed", - strings_processed(), strings_removed()); - if (_process_strings) { - StringTable::finish_dead_counter(); - } - } - - void work(uint worker_id) { - int strings_processed = 0; - int strings_removed = 0; - if (_process_strings) { - StringTable::possibly_parallel_unlink(&_par_state_string, _is_alive, &strings_processed, &strings_removed); - Atomic::add(strings_processed, &_strings_processed); - Atomic::add(strings_removed, &_strings_removed); - } - if (_process_string_dedup) { - G1StringDedup::parallel_unlink(&_dedup_closure, worker_id); - } - } - - size_t strings_processed() const { return (size_t)_strings_processed; } - size_t strings_removed() const { return (size_t)_strings_removed; } -}; - -class G1CodeCacheUnloadingTask { -private: - static Monitor* _lock; - - BoolObjectClosure* const _is_alive; - const bool _unloading_occurred; - const uint _num_workers; - - // Variables used to claim nmethods. - CompiledMethod* _first_nmethod; - CompiledMethod* volatile _claimed_nmethod; - - // The list of nmethods that need to be processed by the second pass. - CompiledMethod* volatile _postponed_list; - volatile uint _num_entered_barrier; - - public: - G1CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred) : - _is_alive(is_alive), - _unloading_occurred(unloading_occurred), - _num_workers(num_workers), - _first_nmethod(NULL), - _claimed_nmethod(NULL), - _postponed_list(NULL), - _num_entered_barrier(0) - { - CompiledMethod::increase_unloading_clock(); - // Get first alive nmethod - CompiledMethodIterator iter = CompiledMethodIterator(); - if(iter.next_alive()) { - _first_nmethod = iter.method(); - } - _claimed_nmethod = _first_nmethod; - } - - ~G1CodeCacheUnloadingTask() { - CodeCache::verify_clean_inline_caches(); - - CodeCache::set_needs_cache_clean(false); - guarantee(CodeCache::scavenge_root_nmethods() == NULL, "Must be"); - - CodeCache::verify_icholder_relocations(); - } - - private: - void add_to_postponed_list(CompiledMethod* nm) { - CompiledMethod* old; - do { - old = _postponed_list; - nm->set_unloading_next(old); - } while (Atomic::cmpxchg(nm, &_postponed_list, old) != old); - } - - void clean_nmethod(CompiledMethod* nm) { - bool postponed = nm->do_unloading_parallel(_is_alive, _unloading_occurred); - - if (postponed) { - // This nmethod referred to an nmethod that has not been cleaned/unloaded yet. - add_to_postponed_list(nm); - } - - // Mark that this nmethod has been cleaned/unloaded. - // After this call, it will be safe to ask if this nmethod was unloaded or not. - nm->set_unloading_clock(CompiledMethod::global_unloading_clock()); - } - - void clean_nmethod_postponed(CompiledMethod* nm) { - nm->do_unloading_parallel_postponed(); - } - - static const int MaxClaimNmethods = 16; - - void claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods) { - CompiledMethod* first; - CompiledMethodIterator last; - - do { - *num_claimed_nmethods = 0; - - first = _claimed_nmethod; - last = CompiledMethodIterator(first); - - if (first != NULL) { - - for (int i = 0; i < MaxClaimNmethods; i++) { - if (!last.next_alive()) { - break; - } - claimed_nmethods[i] = last.method(); - (*num_claimed_nmethods)++; - } - } - - } while (Atomic::cmpxchg(last.method(), &_claimed_nmethod, first) != first); - } - - CompiledMethod* claim_postponed_nmethod() { - CompiledMethod* claim; - CompiledMethod* next; - - do { - claim = _postponed_list; - if (claim == NULL) { - return NULL; - } - - next = claim->unloading_next(); - - } while (Atomic::cmpxchg(next, &_postponed_list, claim) != claim); - - return claim; - } - - public: - // Mark that we're done with the first pass of nmethod cleaning. - void barrier_mark(uint worker_id) { - MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); - _num_entered_barrier++; - if (_num_entered_barrier == _num_workers) { - ml.notify_all(); - } - } - - // See if we have to wait for the other workers to - // finish their first-pass nmethod cleaning work. - void barrier_wait(uint worker_id) { - if (_num_entered_barrier < _num_workers) { - MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); - while (_num_entered_barrier < _num_workers) { - ml.wait(Mutex::_no_safepoint_check_flag, 0, false); - } - } - } - - // Cleaning and unloading of nmethods. Some work has to be postponed - // to the second pass, when we know which nmethods survive. - void work_first_pass(uint worker_id) { - // The first nmethods is claimed by the first worker. - if (worker_id == 0 && _first_nmethod != NULL) { - clean_nmethod(_first_nmethod); - _first_nmethod = NULL; - } - - int num_claimed_nmethods; - CompiledMethod* claimed_nmethods[MaxClaimNmethods]; - - while (true) { - claim_nmethods(claimed_nmethods, &num_claimed_nmethods); - - if (num_claimed_nmethods == 0) { - break; - } - - for (int i = 0; i < num_claimed_nmethods; i++) { - clean_nmethod(claimed_nmethods[i]); - } - } - } - - void work_second_pass(uint worker_id) { - CompiledMethod* nm; - // Take care of postponed nmethods. - while ((nm = claim_postponed_nmethod()) != NULL) { - clean_nmethod_postponed(nm); - } - } -}; - -Monitor* G1CodeCacheUnloadingTask::_lock = new Monitor(Mutex::leaf, "Code Cache Unload lock", false, Monitor::_safepoint_check_never); - -class G1KlassCleaningTask : public StackObj { - volatile int _clean_klass_tree_claimed; - ClassLoaderDataGraphKlassIteratorAtomic _klass_iterator; - - public: - G1KlassCleaningTask() : - _clean_klass_tree_claimed(0), - _klass_iterator() { - } - - private: - bool claim_clean_klass_tree_task() { - if (_clean_klass_tree_claimed) { - return false; - } - - return Atomic::cmpxchg(1, &_clean_klass_tree_claimed, 0) == 0; - } - - InstanceKlass* claim_next_klass() { - Klass* klass; - do { - klass =_klass_iterator.next_klass(); - } while (klass != NULL && !klass->is_instance_klass()); - - // this can be null so don't call InstanceKlass::cast - return static_cast(klass); - } - -public: - - void clean_klass(InstanceKlass* ik) { - ik->clean_weak_instanceklass_links(); - } - - void work() { - ResourceMark rm; - - // One worker will clean the subklass/sibling klass tree. - if (claim_clean_klass_tree_task()) { - Klass::clean_subklass_tree(); - } - - // All workers will help cleaning the classes, - InstanceKlass* klass; - while ((klass = claim_next_klass()) != NULL) { - clean_klass(klass); - } - } -}; - -// To minimize the remark pause times, the tasks below are done in parallel. -class G1ParallelCleaningTask : public AbstractGangTask { -private: - bool _unloading_occurred; - G1StringCleaningTask _string_task; - G1CodeCacheUnloadingTask _code_cache_task; - G1KlassCleaningTask _klass_cleaning_task; - -public: - // The constructor is run in the VMThread. - G1ParallelCleaningTask(BoolObjectClosure* is_alive, uint num_workers, bool unloading_occurred) : - AbstractGangTask("Parallel Cleaning"), - _unloading_occurred(unloading_occurred), - _string_task(is_alive, true, G1StringDedup::is_enabled()), - _code_cache_task(num_workers, is_alive, unloading_occurred), - _klass_cleaning_task() { - } - - // The parallel work done by all worker threads. - void work(uint worker_id) { - // Do first pass of code cache cleaning. - _code_cache_task.work_first_pass(worker_id); - - // Let the threads mark that the first pass is done. - _code_cache_task.barrier_mark(worker_id); - - // Clean the Strings. - _string_task.work(worker_id); - - // Wait for all workers to finish the first code cache cleaning pass. - _code_cache_task.barrier_wait(worker_id); - - // Do the second code cache cleaning work, which realize on - // the liveness information gathered during the first pass. - _code_cache_task.work_second_pass(worker_id); - - // Clean all klasses that were not unloaded. - // The weak metadata in klass doesn't need to be - // processed if there was no unloading. - if (_unloading_occurred) { - _klass_cleaning_task.work(); - } - } -}; - - void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive, bool class_unloading_occurred) { uint n_workers = workers()->active_workers(); - G1ParallelCleaningTask g1_unlink_task(is_alive, n_workers, class_unloading_occurred); + G1StringDedupUnlinkOrOopsDoClosure dedup_closure(is_alive, NULL, false); + ParallelCleaningTask g1_unlink_task(is_alive, &dedup_closure, n_workers, class_unloading_occurred); workers()->run_task(&g1_unlink_task); } @@ -3593,7 +3272,8 @@ return; } - G1StringCleaningTask g1_unlink_task(is_alive, process_strings, process_string_dedup); + G1StringDedupUnlinkOrOopsDoClosure dedup_closure(is_alive, NULL, false); + StringCleaningTask g1_unlink_task(is_alive, process_string_dedup ? &dedup_closure : NULL, process_strings); workers()->run_task(&g1_unlink_task); } diff -r 492b366f8e57 -r 6c0ac4b4d761 src/hotspot/share/gc/shared/parallelCleaning.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp Tue Aug 21 20:29:57 2018 -0400 @@ -0,0 +1,337 @@ +/* + * 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 "classfile/symbolTable.hpp" +#include "classfile/stringTable.hpp" +#include "code/codeCache.hpp" +#include "gc/shared/parallelCleaning.hpp" +#include "memory/resourceArea.hpp" +#include "prims/resolvedMethodTable.hpp" +#include "logging/log.hpp" + +StringCleaningTask::StringCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure, bool process_strings) : + AbstractGangTask("String Unlinking"), + _is_alive(is_alive), + _dedup_closure(dedup_closure), + _par_state_string(StringTable::weak_storage()), + _initial_string_table_size((int) StringTable::the_table()->table_size()), + _process_strings(process_strings), _strings_processed(0), _strings_removed(0) { + + if (process_strings) { + StringTable::reset_dead_counter(); + } +} + +StringCleaningTask::~StringCleaningTask() { + log_info(gc, stringtable)( + "Cleaned string table, " + "strings: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed", + strings_processed(), strings_removed()); + if (_process_strings) { + StringTable::finish_dead_counter(); + } +} + +void StringCleaningTask::work(uint worker_id) { + int strings_processed = 0; + int strings_removed = 0; + if (_process_strings) { + StringTable::possibly_parallel_unlink(&_par_state_string, _is_alive, &strings_processed, &strings_removed); + Atomic::add(strings_processed, &_strings_processed); + Atomic::add(strings_removed, &_strings_removed); + } + if (_dedup_closure != NULL) { + StringDedup::parallel_unlink(_dedup_closure, worker_id); + } +} + +CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred) : + _is_alive(is_alive), + _unloading_occurred(unloading_occurred), + _num_workers(num_workers), + _first_nmethod(NULL), + _claimed_nmethod(NULL), + _postponed_list(NULL), + _num_entered_barrier(0) { + CompiledMethod::increase_unloading_clock(); + // Get first alive nmethod + CompiledMethodIterator iter = CompiledMethodIterator(); + if(iter.next_alive()) { + _first_nmethod = iter.method(); + } + _claimed_nmethod = _first_nmethod; +} + +CodeCacheUnloadingTask::~CodeCacheUnloadingTask() { + CodeCache::verify_clean_inline_caches(); + + CodeCache::set_needs_cache_clean(false); + guarantee(CodeCache::scavenge_root_nmethods() == NULL, "Must be"); + + CodeCache::verify_icholder_relocations(); +} + +Monitor* CodeCacheUnloadingTask::_lock = new Monitor(Mutex::leaf, "Code Cache Unload lock", false, Monitor::_safepoint_check_never); + +void CodeCacheUnloadingTask::add_to_postponed_list(CompiledMethod* nm) { + CompiledMethod* old; + do { + old = _postponed_list; + nm->set_unloading_next(old); + } while (Atomic::cmpxchg(nm, &_postponed_list, old) != old); +} + +void CodeCacheUnloadingTask::clean_nmethod(CompiledMethod* nm) { + bool postponed = nm->do_unloading_parallel(_is_alive, _unloading_occurred); + + if (postponed) { + // This nmethod referred to an nmethod that has not been cleaned/unloaded yet. + add_to_postponed_list(nm); + } + + // Mark that this nmethod has been cleaned/unloaded. + // After this call, it will be safe to ask if this nmethod was unloaded or not. + nm->set_unloading_clock(CompiledMethod::global_unloading_clock()); +} + +void CodeCacheUnloadingTask::clean_nmethod_postponed(CompiledMethod* nm) { + nm->do_unloading_parallel_postponed(); +} + +void CodeCacheUnloadingTask::claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods) { + CompiledMethod* first; + CompiledMethodIterator last; + + do { + *num_claimed_nmethods = 0; + + first = _claimed_nmethod; + last = CompiledMethodIterator(first); + + if (first != NULL) { + + for (int i = 0; i < MaxClaimNmethods; i++) { + if (!last.next_alive()) { + break; + } + claimed_nmethods[i] = last.method(); + (*num_claimed_nmethods)++; + } + } + + } while (Atomic::cmpxchg(last.method(), &_claimed_nmethod, first) != first); +} + +CompiledMethod* CodeCacheUnloadingTask::claim_postponed_nmethod() { + CompiledMethod* claim; + CompiledMethod* next; + + do { + claim = _postponed_list; + if (claim == NULL) { + return NULL; + } + + next = claim->unloading_next(); + + } while (Atomic::cmpxchg(next, &_postponed_list, claim) != claim); + + return claim; +} + +void CodeCacheUnloadingTask::barrier_mark(uint worker_id) { + MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); + _num_entered_barrier++; + if (_num_entered_barrier == _num_workers) { + ml.notify_all(); + } +} + +void CodeCacheUnloadingTask::barrier_wait(uint worker_id) { + if (_num_entered_barrier < _num_workers) { + MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); + while (_num_entered_barrier < _num_workers) { + ml.wait(Mutex::_no_safepoint_check_flag, 0, false); + } + } +} + +void CodeCacheUnloadingTask::work_first_pass(uint worker_id) { + // The first nmethods is claimed by the first worker. + if (worker_id == 0 && _first_nmethod != NULL) { + clean_nmethod(_first_nmethod); + _first_nmethod = NULL; + } + + int num_claimed_nmethods; + CompiledMethod* claimed_nmethods[MaxClaimNmethods]; + + while (true) { + claim_nmethods(claimed_nmethods, &num_claimed_nmethods); + + if (num_claimed_nmethods == 0) { + break; + } + + for (int i = 0; i < num_claimed_nmethods; i++) { + clean_nmethod(claimed_nmethods[i]); + } + } +} + +void CodeCacheUnloadingTask::work_second_pass(uint worker_id) { + CompiledMethod* nm; + // Take care of postponed nmethods. + while ((nm = claim_postponed_nmethod()) != NULL) { + clean_nmethod_postponed(nm); + } +} + + +KlassCleaningTask::KlassCleaningTask() : + _clean_klass_tree_claimed(0), + _klass_iterator() { +} + +bool KlassCleaningTask::claim_clean_klass_tree_task() { + if (_clean_klass_tree_claimed) { + return false; + } + + return Atomic::cmpxchg(1, &_clean_klass_tree_claimed, 0) == 0; +} + +InstanceKlass* KlassCleaningTask::claim_next_klass() { + Klass* klass; + do { + klass =_klass_iterator.next_klass(); + } while (klass != NULL && !klass->is_instance_klass()); + + // this can be null so don't call InstanceKlass::cast + return static_cast(klass); +} + +void KlassCleaningTask::work() { + ResourceMark rm; + + // One worker will clean the subklass/sibling klass tree. + if (claim_clean_klass_tree_task()) { + Klass::clean_subklass_tree(); + } + + // All workers will help cleaning the classes, + InstanceKlass* klass; + while ((klass = claim_next_klass()) != NULL) { + clean_klass(klass); + } +} + +bool ResolvedMethodCleaningTask::claim_resolved_method_task() { + if (_resolved_method_task_claimed) { + return false; + } + return Atomic::cmpxchg(1, &_resolved_method_task_claimed, 0) == 0; +} + +// These aren't big, one thread can do it all. +void ResolvedMethodCleaningTask::work() { + if (claim_resolved_method_task()) { + ResolvedMethodTable::unlink(); + } +} + +class ParallelCleaningTaskTimer { + volatile jint* _timer_us; + jlong start; +public: + ParallelCleaningTaskTimer(jint* timer_us) : _timer_us(timer_us) { + start = os::javaTimeNanos(); + } + ~ParallelCleaningTaskTimer() { + jlong ns = os::javaTimeNanos() - start; + jlong us = ns / 1000; + assert (us < max_jint, "overflow"); + Atomic::add((jint) us, _timer_us); + } +}; + + +ParallelCleaningTask::ParallelCleaningTask(BoolObjectClosure* is_alive, + StringDedupUnlinkOrOopsDoClosure* dedup_closure, uint num_workers, bool unloading_occurred) : + AbstractGangTask("Parallel Cleaning"), + _unloading_occurred(unloading_occurred), + _string_task(is_alive, StringDedup::is_enabled() ? dedup_closure : NULL, true), + _code_cache_task(num_workers, is_alive, unloading_occurred), + _klass_cleaning_task(), + _resolved_method_cleaning_task() { +} + +// The parallel work done by all worker threads. +void ParallelCleaningTask::work(uint worker_id) { + { + ParallelCleaningTaskTimer timer(&_times._codecache_work); + // Do first pass of code cache cleaning. + _code_cache_task.work_first_pass(worker_id); + } + + { + ParallelCleaningTaskTimer timer(&_times._sync); + // Let the threads mark that the first pass is done. + _code_cache_task.barrier_mark(worker_id); + } + + { + ParallelCleaningTaskTimer timer(&_times._tables_work); + // Clean the Strings and Symbols. + _string_task.work(worker_id); + } + + { + ParallelCleaningTaskTimer timer(&_times._rmt_work); + // Clean unreferenced things in the ResolvedMethodTable + _resolved_method_cleaning_task.work(); + } + + { + ParallelCleaningTaskTimer timer(&_times._sync); + // Wait for all workers to finish the first code cache cleaning pass. + _code_cache_task.barrier_wait(worker_id); + } + + { + ParallelCleaningTaskTimer timer(&_times._codecache_work); + // Do the second code cache cleaning work, which realize on + // the liveness information gathered during the first pass. + _code_cache_task.work_second_pass(worker_id); + } + + // Clean all klasses that were not unloaded. + // The weak metadata in klass doesn't need to be + // processed if there was no unloading. + if (_unloading_occurred) { + ParallelCleaningTaskTimer timer(&_times._klass_work); + _klass_cleaning_task.work(); + } +} diff -r 492b366f8e57 -r 6c0ac4b4d761 src/hotspot/share/gc/shared/parallelCleaning.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp Tue Aug 21 20:29:57 2018 -0400 @@ -0,0 +1,175 @@ +/* + * 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_PARALLELCLEANING_HPP +#define SHARE_VM_GC_SHARED_PARALLELCLEANING_HPP + +#include "gc/shared/oopStorageParState.hpp" +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/workgroup.hpp" + +class ParallelCleaningTask; + +class StringCleaningTask : public AbstractGangTask { +private: + BoolObjectClosure* _is_alive; + StringDedupUnlinkOrOopsDoClosure * const _dedup_closure; + + OopStorage::ParState _par_state_string; + + int _initial_string_table_size; + + bool _process_strings; + int _strings_processed; + int _strings_removed; + +public: + StringCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure, bool process_strings); + ~StringCleaningTask(); + + void work(uint worker_id); + + size_t strings_processed() const { return (size_t)_strings_processed; } + size_t strings_removed() const { return (size_t)_strings_removed; } +}; + +class CodeCacheUnloadingTask { +private: + static Monitor* _lock; + + BoolObjectClosure* const _is_alive; + const bool _unloading_occurred; + const uint _num_workers; + + // Variables used to claim nmethods. + CompiledMethod* _first_nmethod; + CompiledMethod* volatile _claimed_nmethod; + + // The list of nmethods that need to be processed by the second pass. + CompiledMethod* volatile _postponed_list; + volatile uint _num_entered_barrier; + +public: + CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred); + ~CodeCacheUnloadingTask(); + +private: + void add_to_postponed_list(CompiledMethod* nm); + void clean_nmethod(CompiledMethod* nm); + void clean_nmethod_postponed(CompiledMethod* nm); + + static const int MaxClaimNmethods = 16; + + void claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods); + CompiledMethod* claim_postponed_nmethod(); +public: + // Mark that we're done with the first pass of nmethod cleaning. + void barrier_mark(uint worker_id); + + // See if we have to wait for the other workers to + // finish their first-pass nmethod cleaning work. + void barrier_wait(uint worker_id); + + // Cleaning and unloading of nmethods. Some work has to be postponed + // to the second pass, when we know which nmethods survive. + void work_first_pass(uint worker_id); + void work_second_pass(uint worker_id); +}; + + +class KlassCleaningTask : public StackObj { + volatile int _clean_klass_tree_claimed; + ClassLoaderDataGraphKlassIteratorAtomic _klass_iterator; + +public: + KlassCleaningTask(); + +private: + bool claim_clean_klass_tree_task(); + InstanceKlass* claim_next_klass(); + +public: + + void clean_klass(InstanceKlass* ik) { + ik->clean_weak_instanceklass_links(); + } + + void work(); +}; + +class ResolvedMethodCleaningTask : public StackObj { + volatile int _resolved_method_task_claimed; +public: + ResolvedMethodCleaningTask() : + _resolved_method_task_claimed(0) {} + + bool claim_resolved_method_task(); + + void work(); +}; + +class ParallelCleaningTimes { + friend class ParallelCleaningTask; +private: + // All times are in microseconds, making room for ~2 hrs in jint + jint _sync, _codecache_work, _tables_work, _rmt_work, _klass_work; + +public: + ParallelCleaningTimes() : + _sync(0), + _codecache_work(0), + _tables_work(0), + _rmt_work(0), + _klass_work(0) {}; + + jint sync_us() const { return _sync; } + jint codecache_work_us() const { return _codecache_work; } + jint tables_work_us() const { return _tables_work; } + jint rmt_work_us() const { return _rmt_work; } + jint klass_work_us() const { return _klass_work; } +}; + +// To minimize the remark pause times, the tasks below are done in parallel. +class ParallelCleaningTask : public AbstractGangTask { +private: + bool _unloading_occurred; + StringCleaningTask _string_task; + CodeCacheUnloadingTask _code_cache_task; + KlassCleaningTask _klass_cleaning_task; + ResolvedMethodCleaningTask _resolved_method_cleaning_task; + + ParallelCleaningTimes _times; +public: + // The constructor is run in the VMThread. + ParallelCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure, + uint num_workers, bool unloading_occurred); + + void work(uint worker_id); + + ParallelCleaningTimes times() const { + return _times; + } +}; + +#endif // SHARE_VM_GC_SHARED_PARALLELCLEANING_HPP