ysr@777: /* johnc@3891: * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. ysr@777: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ysr@777: * ysr@777: * This code is free software; you can redistribute it and/or modify it ysr@777: * under the terms of the GNU General Public License version 2 only, as ysr@777: * published by the Free Software Foundation. ysr@777: * ysr@777: * This code is distributed in the hope that it will be useful, but WITHOUT ysr@777: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ysr@777: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ysr@777: * version 2 for more details (a copy is included in the LICENSE file that ysr@777: * accompanied this code). ysr@777: * ysr@777: * You should have received a copy of the GNU General Public License version ysr@777: * 2 along with this work; if not, write to the Free Software Foundation, ysr@777: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ysr@777: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. ysr@777: * ysr@777: */ ysr@777: stefank@2314: #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONREMSET_HPP stefank@2314: #define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONREMSET_HPP stefank@2314: stefank@2314: #include "gc_implementation/g1/sparsePRT.hpp" stefank@2314: ysr@777: // Remembered set for a heap region. Represent a set of "cards" that ysr@777: // contain pointers into the owner heap region. Cards are defined somewhat ysr@777: // abstractly, in terms of what the "BlockOffsetTable" in use can parse. ysr@777: ysr@777: class G1CollectedHeap; ysr@777: class G1BlockOffsetSharedArray; ysr@777: class HeapRegion; ysr@777: class HeapRegionRemSetIterator; johnc@3891: class PerRegionTable; ysr@777: class SparsePRT; ysr@777: tonyp@2493: // Essentially a wrapper around SparsePRTCleanupTask. See tonyp@2493: // sparsePRT.hpp for more details. tonyp@2493: class HRRSCleanupTask : public SparsePRTCleanupTask { tonyp@2493: }; ysr@777: ysr@777: // The "_coarse_map" is a bitmap with one bit for each region, where set ysr@777: // bits indicate that the corresponding region may contain some pointer ysr@777: // into the owning region. ysr@777: ysr@777: // The "_fine_grain_entries" array is an open hash table of PerRegionTables ysr@777: // (PRTs), indicating regions for which we're keeping the RS as a set of ysr@777: // cards. The strategy is to cap the size of the fine-grain table, ysr@777: // deleting an entry and setting the corresponding coarse-grained bit when ysr@777: // we would overflow this cap. ysr@777: ysr@777: // We use a mixture of locking and lock-free techniques here. We allow ysr@777: // threads to locate PRTs without locking, but threads attempting to alter ysr@777: // a bucket list obtain a lock. This means that any failing attempt to ysr@777: // find a PRT must be retried with the lock. It might seem dangerous that ysr@777: // a read can find a PRT that is concurrently deleted. This is all right, ysr@777: // because: ysr@777: // ysr@777: // 1) We only actually free PRT's at safe points (though we reuse them at ysr@777: // other times). ysr@777: // 2) We find PRT's in an attempt to add entries. If a PRT is deleted, ysr@777: // it's _coarse_map bit is set, so the that we were attempting to add ysr@777: // is represented. If a deleted PRT is re-used, a thread adding a bit, ysr@777: // thinking the PRT is for a different region, does no harm. ysr@777: apetrusenko@984: class OtherRegionsTable VALUE_OBJ_CLASS_SPEC { ysr@777: friend class HeapRegionRemSetIterator; ysr@777: ysr@777: G1CollectedHeap* _g1h; ysr@777: Mutex _m; ysr@777: HeapRegion* _hr; ysr@777: ysr@777: // These are protected by "_m". ysr@777: BitMap _coarse_map; ysr@777: size_t _n_coarse_entries; ysr@777: static jint _n_coarsenings; ysr@777: johnc@3891: PerRegionTable** _fine_grain_regions; johnc@3891: size_t _n_fine_entries; ysr@777: johnc@3956: // The fine grain remembered sets are doubly linked together using johnc@3956: // their 'next' and 'prev' fields. johnc@3956: // This allows fast bulk freeing of all the fine grain remembered johnc@3956: // set entries, and fast finding of all of them without iterating johnc@3956: // over the _fine_grain_regions table. johnc@3956: PerRegionTable * _first_all_fine_prts; johnc@3956: PerRegionTable * _last_all_fine_prts; johnc@3956: johnc@3891: // Used to sample a subset of the fine grain PRTs to determine which johnc@3891: // PRT to evict and coarsen. ysr@777: size_t _fine_eviction_start; ysr@777: static size_t _fine_eviction_stride; ysr@777: static size_t _fine_eviction_sample_size; ysr@777: ysr@777: SparsePRT _sparse_table; ysr@777: ysr@777: // These are static after init. ysr@777: static size_t _max_fine_entries; ysr@777: static size_t _mod_max_fine_entries_mask; ysr@777: ysr@777: // Requires "prt" to be the first element of the bucket list appropriate ysr@777: // for "hr". If this list contains an entry for "hr", return it, ysr@777: // otherwise return "NULL". johnc@3891: PerRegionTable* find_region_table(size_t ind, HeapRegion* hr) const; ysr@777: johnc@3891: // Find, delete, and return a candidate PerRegionTable, if any exists, ysr@777: // adding the deleted region to the coarse bitmap. Requires the caller ysr@777: // to hold _m, and the fine-grain table to be full. johnc@3891: PerRegionTable* delete_region_table(); ysr@777: ysr@777: // If a PRT for "hr" is in the bucket list indicated by "ind" (which must ysr@777: // be the correct index for "hr"), delete it and return true; else return ysr@777: // false. ysr@777: bool del_single_region_table(size_t ind, HeapRegion* hr); ysr@777: ysr@777: // Indexed by thread X heap region, to minimize thread contention. ysr@777: static int** _from_card_cache; ysr@777: static size_t _from_card_cache_max_regions; ysr@777: static size_t _from_card_cache_mem_size; ysr@777: johnc@3956: // link/add the given fine grain remembered set into the "all" list johnc@3956: void link_to_all(PerRegionTable * prt); johnc@3956: // unlink/remove the given fine grain remembered set into the "all" list johnc@3956: void unlink_from_all(PerRegionTable * prt); johnc@3956: ysr@777: public: ysr@777: OtherRegionsTable(HeapRegion* hr); ysr@777: ysr@777: HeapRegion* hr() const { return _hr; } ysr@777: ysr@777: // For now. Could "expand" some tables in the future, so that this made ysr@777: // sense. ysr@1280: void add_reference(OopOrNarrowOopStar from, int tid); ysr@777: ysr@777: // Removes any entries shown by the given bitmaps to contain only dead ysr@777: // objects. ysr@777: void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); ysr@777: ysr@777: // Not const because it takes a lock. ysr@777: size_t occupied() const; ysr@777: size_t occ_fine() const; ysr@777: size_t occ_coarse() const; ysr@777: size_t occ_sparse() const; ysr@777: ysr@777: static jint n_coarsenings() { return _n_coarsenings; } ysr@777: ysr@777: // Returns size in bytes. ysr@777: // Not const because it takes a lock. ysr@777: size_t mem_size() const; ysr@777: static size_t static_mem_size(); ysr@777: static size_t fl_mem_size(); ysr@777: ysr@1280: bool contains_reference(OopOrNarrowOopStar from) const; ysr@1280: bool contains_reference_locked(OopOrNarrowOopStar from) const; ysr@777: ysr@777: void clear(); ysr@777: ysr@777: // Specifically clear the from_card_cache. ysr@777: void clear_fcc(); ysr@777: ysr@777: // "from_hr" is being cleared; remove any entries from it. ysr@777: void clear_incoming_entry(HeapRegion* from_hr); ysr@777: tonyp@2493: void do_cleanup_work(HRRSCleanupTask* hrrs_cleanup_task); tonyp@2493: ysr@777: // Declare the heap size (in # of regions) to the OtherRegionsTable. ysr@777: // (Uses it to initialize from_card_cache). ysr@777: static void init_from_card_cache(size_t max_regions); ysr@777: ysr@777: // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. ysr@777: // Make sure any entries for higher regions are invalid. ysr@777: static void shrink_from_card_cache(size_t new_n_regs); ysr@777: ysr@777: static void print_from_card_cache(); ysr@777: }; ysr@777: zgu@3900: class HeapRegionRemSet : public CHeapObj { ysr@777: friend class VMStructs; ysr@777: friend class HeapRegionRemSetIterator; ysr@777: ysr@777: public: ysr@777: enum Event { ysr@777: Event_EvacStart, Event_EvacEnd, Event_RSUpdateEnd ysr@777: }; ysr@777: ysr@777: private: ysr@777: G1BlockOffsetSharedArray* _bosa; ysr@777: G1BlockOffsetSharedArray* bosa() const { return _bosa; } ysr@777: ysr@777: OtherRegionsTable _other_regions; ysr@777: ysr@777: enum ParIterState { Unclaimed, Claimed, Complete }; iveresov@1696: volatile ParIterState _iter_state; iveresov@1696: volatile jlong _iter_claimed; ysr@777: ysr@777: // Unused unless G1RecordHRRSOops is true. ysr@777: ysr@777: static const int MaxRecorded = 1000000; ysr@1280: static OopOrNarrowOopStar* _recorded_oops; ysr@1280: static HeapWord** _recorded_cards; ysr@1280: static HeapRegion** _recorded_regions; ysr@1280: static int _n_recorded; ysr@777: ysr@777: static const int MaxRecordedEvents = 1000; ysr@777: static Event* _recorded_events; ysr@777: static int* _recorded_event_index; ysr@777: static int _n_recorded_events; ysr@777: ysr@777: static void print_event(outputStream* str, Event evnt); ysr@777: ysr@777: public: ysr@777: HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, ysr@777: HeapRegion* hr); ysr@777: ysr@777: static int num_par_rem_sets(); iveresov@1696: static void setup_remset_size(); ysr@777: ysr@777: HeapRegion* hr() const { ysr@777: return _other_regions.hr(); ysr@777: } ysr@777: ysr@777: size_t occupied() const { ysr@777: return _other_regions.occupied(); ysr@777: } ysr@777: size_t occ_fine() const { ysr@777: return _other_regions.occ_fine(); ysr@777: } ysr@777: size_t occ_coarse() const { ysr@777: return _other_regions.occ_coarse(); ysr@777: } ysr@777: size_t occ_sparse() const { ysr@777: return _other_regions.occ_sparse(); ysr@777: } ysr@777: ysr@777: static jint n_coarsenings() { return OtherRegionsTable::n_coarsenings(); } ysr@777: johnc@3891: // Used in the sequential case. ysr@1280: void add_reference(OopOrNarrowOopStar from) { johnc@3891: _other_regions.add_reference(from, 0); ysr@777: } ysr@777: johnc@3891: // Used in the parallel case. ysr@1280: void add_reference(OopOrNarrowOopStar from, int tid) { ysr@777: _other_regions.add_reference(from, tid); ysr@777: } ysr@777: ysr@777: // Removes any entries shown by the given bitmaps to contain only dead ysr@777: // objects. ysr@777: void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); ysr@777: ysr@777: // The region is being reclaimed; clear its remset, and any mention of ysr@777: // entries for this region in other remsets. ysr@777: void clear(); ysr@777: ysr@777: // Attempt to claim the region. Returns true iff this call caused an ysr@777: // atomic transition from Unclaimed to Claimed. ysr@777: bool claim_iter(); ysr@777: // Sets the iteration state to "complete". ysr@777: void set_iter_complete(); ysr@777: // Returns "true" iff the region's iteration is complete. ysr@777: bool iter_is_complete(); ysr@777: iveresov@1696: // Support for claiming blocks of cards during iteration iveresov@1696: size_t iter_claimed() const { return (size_t)_iter_claimed; } iveresov@1696: // Claim the next block of cards iveresov@1696: size_t iter_claimed_next(size_t step) { iveresov@1696: size_t current, next; iveresov@1696: do { iveresov@1696: current = iter_claimed(); iveresov@1696: next = current + step; iveresov@1696: } while (Atomic::cmpxchg((jlong)next, &_iter_claimed, (jlong)current) != (jlong)current); iveresov@1696: return current; iveresov@1696: } tonyp@2974: void reset_for_par_iteration(); tonyp@2974: tonyp@2974: bool verify_ready_for_par_iteration() { tonyp@2974: return (_iter_state == Unclaimed) && (_iter_claimed == 0); tonyp@2974: } iveresov@1696: ysr@777: // Initialize the given iterator to iterate over this rem set. ysr@777: void init_iterator(HeapRegionRemSetIterator* iter) const; ysr@777: ysr@777: // The actual # of bytes this hr_remset takes up. ysr@777: size_t mem_size() { ysr@777: return _other_regions.mem_size() ysr@777: // This correction is necessary because the above includes the second ysr@777: // part. ysr@777: + sizeof(this) - sizeof(OtherRegionsTable); ysr@777: } ysr@777: ysr@777: // Returns the memory occupancy of all static data structures associated ysr@777: // with remembered sets. ysr@777: static size_t static_mem_size() { ysr@777: return OtherRegionsTable::static_mem_size(); ysr@777: } ysr@777: ysr@777: // Returns the memory occupancy of all free_list data structures associated ysr@777: // with remembered sets. ysr@777: static size_t fl_mem_size() { ysr@777: return OtherRegionsTable::fl_mem_size(); ysr@777: } ysr@777: ysr@1280: bool contains_reference(OopOrNarrowOopStar from) const { ysr@777: return _other_regions.contains_reference(from); ysr@777: } ysr@777: void print() const; ysr@777: ysr@777: // Called during a stop-world phase to perform any deferred cleanups. ysr@777: static void cleanup(); ysr@777: ysr@777: // Declare the heap size (in # of regions) to the HeapRegionRemSet(s). ysr@777: // (Uses it to initialize from_card_cache). tonyp@3713: static void init_heap(uint max_regions) { tonyp@3713: OtherRegionsTable::init_from_card_cache((size_t) max_regions); ysr@777: } ysr@777: ysr@777: // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. tonyp@3713: static void shrink_heap(uint new_n_regs) { tonyp@3713: OtherRegionsTable::shrink_from_card_cache((size_t) new_n_regs); ysr@777: } ysr@777: ysr@777: #ifndef PRODUCT ysr@777: static void print_from_card_cache() { ysr@777: OtherRegionsTable::print_from_card_cache(); ysr@777: } ysr@777: #endif ysr@777: ysr@1280: static void record(HeapRegion* hr, OopOrNarrowOopStar f); ysr@777: static void print_recorded(); ysr@777: static void record_event(Event evnt); ysr@777: tonyp@2493: // These are wrappers for the similarly-named methods on tonyp@2493: // SparsePRT. Look at sparsePRT.hpp for more details. tonyp@2493: static void reset_for_cleanup_tasks(); tonyp@2493: void do_cleanup_work(HRRSCleanupTask* hrrs_cleanup_task); tonyp@2493: static void finish_cleanup_task(HRRSCleanupTask* hrrs_cleanup_task); tonyp@2493: ysr@777: // Run unit tests. ysr@777: #ifndef PRODUCT ysr@777: static void test(); ysr@777: #endif ysr@777: }; ysr@777: zgu@3900: class HeapRegionRemSetIterator : public CHeapObj { ysr@777: ysr@777: // The region over which we're iterating. ysr@777: const HeapRegionRemSet* _hrrs; ysr@777: ysr@777: // Local caching of HRRS fields. ysr@777: const BitMap* _coarse_map; johnc@3891: PerRegionTable** _fine_grain_regions; ysr@777: ysr@777: G1BlockOffsetSharedArray* _bosa; ysr@777: G1CollectedHeap* _g1h; ysr@777: ysr@777: // The number yielded since initialization. ysr@777: size_t _n_yielded_fine; ysr@777: size_t _n_yielded_coarse; ysr@777: size_t _n_yielded_sparse; ysr@777: ysr@777: // If true we're iterating over the coarse table; if false the fine ysr@777: // table. ysr@777: enum IterState { ysr@777: Sparse, ysr@777: Fine, ysr@777: Coarse ysr@777: }; ysr@777: IterState _is; ysr@777: ysr@777: // In both kinds of iteration, heap offset of first card of current ysr@777: // region. ysr@777: size_t _cur_region_card_offset; ysr@777: // Card offset within cur region. ysr@777: size_t _cur_region_cur_card; ysr@777: ysr@777: // Coarse table iteration fields: ysr@777: ysr@777: // Current region index; johnc@3182: int _coarse_cur_region_index; johnc@3182: size_t _coarse_cur_region_cur_card; ysr@777: ysr@777: bool coarse_has_next(size_t& card_index); ysr@777: ysr@777: // Fine table iteration fields: ysr@777: ysr@777: // Index of bucket-list we're working on. ysr@777: int _fine_array_index; johnc@3891: ysr@777: // Per Region Table we're doing within current bucket list. johnc@3891: PerRegionTable* _fine_cur_prt; ysr@777: ysr@777: /* SparsePRT::*/ SparsePRTIter _sparse_iter; ysr@777: ysr@777: void fine_find_next_non_null_prt(); ysr@777: ysr@777: bool fine_has_next(); ysr@777: bool fine_has_next(size_t& card_index); ysr@777: ysr@777: public: ysr@777: // We require an iterator to be initialized before use, so the ysr@777: // constructor does little. ysr@777: HeapRegionRemSetIterator(); ysr@777: ysr@777: void initialize(const HeapRegionRemSet* hrrs); ysr@777: ysr@777: // If there remains one or more cards to be yielded, returns true and ysr@777: // sets "card_index" to one of those cards (which is then considered ysr@777: // yielded.) Otherwise, returns false (and leaves "card_index" ysr@777: // undefined.) ysr@777: bool has_next(size_t& card_index); ysr@777: ysr@777: size_t n_yielded_fine() { return _n_yielded_fine; } ysr@777: size_t n_yielded_coarse() { return _n_yielded_coarse; } ysr@777: size_t n_yielded_sparse() { return _n_yielded_sparse; } ysr@777: size_t n_yielded() { ysr@777: return n_yielded_fine() + n_yielded_coarse() + n_yielded_sparse(); ysr@777: } ysr@777: }; ysr@777: stefank@2314: #endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONREMSET_HPP