diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp index db5b714a8fc..8cb6b52e264 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.cpp @@ -22,7 +22,6 @@ * */ #include "precompiled.hpp" -#include "jfr/leakprofiler/chains/bitset.hpp" #include "jfr/leakprofiler/chains/bfsClosure.hpp" #include "jfr/leakprofiler/chains/dfsClosure.hpp" #include "jfr/leakprofiler/chains/edge.hpp" @@ -37,7 +36,7 @@ #include "oops/oop.inline.hpp" #include "utilities/align.hpp" -BFSClosure::BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits) : +BFSClosure::BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, JFRBitSet* mark_bits) : _edge_queue(edge_queue), _edge_store(edge_store), _mark_bits(mark_bits), diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp index fad71ecb5b9..0b801cffbfd 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/bfsClosure.hpp @@ -25,9 +25,9 @@ #ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP #define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BFSCLOSURE_HPP +#include "jfr/leakprofiler/chains/jfrbitset.hpp" #include "memory/iterator.hpp" -class BitSet; class Edge; class EdgeStore; class EdgeQueue; @@ -37,7 +37,7 @@ class BFSClosure : public BasicOopIterateClosure { private: EdgeQueue* _edge_queue; EdgeStore* _edge_store; - BitSet* _mark_bits; + JFRBitSet* _mark_bits; const Edge* _current_parent; mutable size_t _current_frontier_level; mutable size_t _next_frontier_idx; @@ -62,7 +62,7 @@ class BFSClosure : public BasicOopIterateClosure { void process_queue(); public: - BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits); + BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, JFRBitSet* mark_bits); void process(); void do_root(const oop* ref); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp b/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp deleted file mode 100644 index 67df3d94d52..00000000000 --- a/src/hotspot/share/jfr/leakprofiler/chains/bitset.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 "jfr/leakprofiler/chains/bitset.hpp" -#include "jfr/recorder/storage/jfrVirtualMemory.hpp" -#include "memory/memRegion.hpp" - -BitSet::BitSet(const MemRegion& covered_region) : - _vmm(NULL), - _region_start(covered_region.start()), - _region_size(covered_region.word_size()) { -} - -BitSet::~BitSet() { - delete _vmm; -} - -bool BitSet::initialize() { - assert(_vmm == NULL, "invariant"); - _vmm = new JfrVirtualMemory(); - if (_vmm == NULL) { - return false; - } - - const BitMap::idx_t bits = _region_size >> LogMinObjAlignment; - const size_t words = bits / BitsPerWord; - const size_t raw_bytes = words * sizeof(BitMap::idx_t); - - // the virtual memory invocation will reserve and commit the entire space - BitMap::bm_word_t* map = (BitMap::bm_word_t*)_vmm->initialize(raw_bytes, raw_bytes); - if (map == NULL) { - return false; - } - _bits = BitMapView(map, bits); - return true; -} - diff --git a/src/hotspot/share/jfr/leakprofiler/chains/bitset.hpp b/src/hotspot/share/jfr/leakprofiler/chains/bitset.hpp deleted file mode 100644 index a6424b92970..00000000000 --- a/src/hotspot/share/jfr/leakprofiler/chains/bitset.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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_JFR_LEAKPROFILER_CHAINS_BITSET_HPP -#define SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP - -#include "memory/allocation.hpp" -#include "oops/oopsHierarchy.hpp" -#include "utilities/bitMap.inline.hpp" - -class JfrVirtualMemory; -class MemRegion; - -class BitSet : public CHeapObj { - private: - JfrVirtualMemory* _vmm; - const HeapWord* const _region_start; - BitMapView _bits; - const size_t _region_size; - - public: - BitSet(const MemRegion& covered_region); - ~BitSet(); - - bool initialize(); - - BitMap::idx_t mark_obj(const HeapWord* addr) { - const BitMap::idx_t bit = addr_to_bit(addr); - _bits.set_bit(bit); - return bit; - } - - BitMap::idx_t mark_obj(oop obj) { - return mark_obj((HeapWord*)obj); - } - - bool is_marked(const HeapWord* addr) const { - return is_marked(addr_to_bit(addr)); - } - - bool is_marked(oop obj) const { - return is_marked((HeapWord*)obj); - } - - BitMap::idx_t size() const { - return _bits.size(); - } - - BitMap::idx_t addr_to_bit(const HeapWord* addr) const { - return pointer_delta(addr, _region_start) >> LogMinObjAlignment; - } - - bool is_marked(const BitMap::idx_t bit) const { - return _bits.at(bit); - } -}; - -#endif // SHARE_VM_JFR_LEAKPROFILER_CHAINS_BITSET_HPP diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp index 0b7264a3d0d..40c833035bb 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp @@ -23,10 +23,10 @@ */ #include "precompiled.hpp" -#include "jfr/leakprofiler/chains/bitset.hpp" #include "jfr/leakprofiler/chains/dfsClosure.hpp" #include "jfr/leakprofiler/chains/edge.hpp" #include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/jfrbitset.hpp" #include "jfr/leakprofiler/chains/rootSetClosure.hpp" #include "jfr/leakprofiler/utilities/granularTimer.hpp" #include "jfr/leakprofiler/utilities/rootType.hpp" @@ -41,7 +41,7 @@ static const size_t max_dfs_depth = 5000; EdgeStore* DFSClosure::_edge_store = NULL; -BitSet* DFSClosure::_mark_bits = NULL; +JFRBitSet* DFSClosure::_mark_bits = NULL; const Edge* DFSClosure::_start_edge = NULL; size_t DFSClosure::_max_depth = max_dfs_depth; bool DFSClosure::_ignore_root_set = false; @@ -59,7 +59,7 @@ DFSClosure::DFSClosure(DFSClosure* parent, size_t depth) : } void DFSClosure::find_leaks_from_edge(EdgeStore* edge_store, - BitSet* mark_bits, + JFRBitSet* mark_bits, const Edge* start_edge) { assert(edge_store != NULL, "invariant"); assert(mark_bits != NULL," invariant"); @@ -77,7 +77,7 @@ void DFSClosure::find_leaks_from_edge(EdgeStore* edge_store, } void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store, - BitSet* mark_bits) { + JFRBitSet* mark_bits) { assert(edge_store != NULL, "invariant"); assert(mark_bits != NULL, "invariant"); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp index 5cce8d1b6d8..0fc816f0860 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp @@ -25,9 +25,9 @@ #ifndef SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP #define SHARE_VM_JFR_LEAKPROFILER_CHAINS_DFSCLOSURE_HPP +#include "jfr/leakprofiler/chains/jfrbitset.hpp" #include "memory/iterator.hpp" -class BitSet; class Edge; class EdgeStore; class EdgeQueue; @@ -36,7 +36,7 @@ class EdgeQueue; class DFSClosure : public BasicOopIterateClosure { private: static EdgeStore* _edge_store; - static BitSet* _mark_bits; + static JFRBitSet* _mark_bits; static const Edge*_start_edge; static size_t _max_depth; static bool _ignore_root_set; @@ -54,8 +54,8 @@ class DFSClosure : public BasicOopIterateClosure { DFSClosure(); public: - static void find_leaks_from_edge(EdgeStore* edge_store, BitSet* mark_bits, const Edge* start_edge); - static void find_leaks_from_root_set(EdgeStore* edge_store, BitSet* mark_bits); + static void find_leaks_from_edge(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge); + static void find_leaks_from_root_set(EdgeStore* edge_store, JFRBitSet* mark_bits); void do_root(const oop* ref); virtual void do_oop(oop* ref); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp new file mode 100644 index 00000000000..b74e320e05a --- /dev/null +++ b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 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_JFR_LEAKPROFILER_JFRBITMAP_HPP +#define SHARE_JFR_LEAKPROFILER_JFRBITMAP_HPP + +#include "memory/allocation.hpp" +#include "utilities/objectBitSet.inline.hpp" + +typedef ObjectBitSet JFRBitSet; + +#endif // SHARE_JFR_LEAKPROFILER_JFRBITMAP_HPP diff --git a/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp b/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp index f2fca092432..f4cf1ca01e2 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp @@ -26,7 +26,6 @@ #include "gc/shared/collectedHeap.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/leakprofiler/chains/bfsClosure.hpp" -#include "jfr/leakprofiler/chains/bitset.hpp" #include "jfr/leakprofiler/chains/dfsClosure.hpp" #include "jfr/leakprofiler/chains/edge.hpp" #include "jfr/leakprofiler/chains/edgeQueue.hpp" @@ -84,7 +83,7 @@ void PathToGcRootsOperation::doit() { // The bitset used for marking is dimensioned as a function of the heap size const MemRegion heap_region = Universe::heap()->reserved_region(); - BitSet mark_bits(heap_region); + JFRBitSet mark_bits; // The edge queue is dimensioned as a fraction of the heap size const size_t edge_queue_reservation_size = edge_queue_memory_reservation(heap_region); @@ -93,7 +92,7 @@ void PathToGcRootsOperation::doit() { // The initialize() routines will attempt to reserve and allocate backing storage memory. // Failure to accommodate will render root chain processing impossible. // As a fallback on failure, just write out the existing samples, flat, without chains. - if (!(mark_bits.initialize() && edge_queue.initialize())) { + if (!edge_queue.initialize()) { log_warning(jfr)("Unable to allocate memory for root chain processing"); return; } diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index aaf6f3f659d..366373a22fc 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -58,11 +58,14 @@ #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "utilities/objectBitSet.inline.hpp" #include "utilities/macros.hpp" #if INCLUDE_ZGC #include "gc/z/zGlobals.hpp" #endif +typedef ObjectBitSet JVMTIBitSet; + // JvmtiTagHashmapEntry // // Each entry encapsulates a reference to the tagged object @@ -1626,141 +1629,6 @@ jvmtiError JvmtiTagMap::get_objects_with_tags(const jlong* tags, return collector.result(count_ptr, object_result_ptr, tag_result_ptr); } - -// ObjectMarker is used to support the marking objects when walking the -// heap. -// -// This implementation uses the existing mark bits in an object for -// marking. Objects that are marked must later have their headers restored. -// As most objects are unlocked and don't have their identity hash computed -// we don't have to save their headers. Instead we save the headers that -// are "interesting". Later when the headers are restored this implementation -// restores all headers to their initial value and then restores the few -// objects that had interesting headers. -// -// Future work: This implementation currently uses growable arrays to save -// the oop and header of interesting objects. As an optimization we could -// use the same technique as the GC and make use of the unused area -// between top() and end(). -// - -// An ObjectClosure used to restore the mark bits of an object -class RestoreMarksClosure : public ObjectClosure { - public: - void do_object(oop o) { - if (o != NULL) { - markOop mark = o->mark(); - if (mark->is_marked()) { - o->init_mark(); - } - } - } -}; - -// ObjectMarker provides the mark and visited functions -class ObjectMarker : AllStatic { - private: - // saved headers - static GrowableArray* _saved_oop_stack; - static GrowableArray* _saved_mark_stack; - static bool _needs_reset; // do we need to reset mark bits? - - public: - static void init(); // initialize - static void done(); // clean-up - - static inline void mark(oop o); // mark an object - static inline bool visited(oop o); // check if object has been visited - - static inline bool needs_reset() { return _needs_reset; } - static inline void set_needs_reset(bool v) { _needs_reset = v; } -}; - -GrowableArray* ObjectMarker::_saved_oop_stack = NULL; -GrowableArray* ObjectMarker::_saved_mark_stack = NULL; -bool ObjectMarker::_needs_reset = true; // need to reset mark bits by default - -// initialize ObjectMarker - prepares for object marking -void ObjectMarker::init() { - assert(Thread::current()->is_VM_thread(), "must be VMThread"); - - // prepare heap for iteration - Universe::heap()->ensure_parsability(false); // no need to retire TLABs - - // create stacks for interesting headers - _saved_mark_stack = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(4000, true); - _saved_oop_stack = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(4000, true); - - if (UseBiasedLocking) { - BiasedLocking::preserve_marks(); - } -} - -// Object marking is done so restore object headers -void ObjectMarker::done() { - // iterate over all objects and restore the mark bits to - // their initial value - RestoreMarksClosure blk; - if (needs_reset()) { - Universe::heap()->object_iterate(&blk); - } else { - // We don't need to reset mark bits on this call, but reset the - // flag to the default for the next call. - set_needs_reset(true); - } - - // now restore the interesting headers - for (int i = 0; i < _saved_oop_stack->length(); i++) { - oop o = _saved_oop_stack->at(i); - markOop mark = _saved_mark_stack->at(i); - o->set_mark(mark); - } - - if (UseBiasedLocking) { - BiasedLocking::restore_marks(); - } - - // free the stacks - delete _saved_oop_stack; - delete _saved_mark_stack; -} - -// mark an object -inline void ObjectMarker::mark(oop o) { - assert(Universe::heap()->is_in(o), "sanity check"); - assert(!o->mark()->is_marked(), "should only mark an object once"); - - // object's mark word - markOop mark = o->mark(); - - if (mark->must_be_preserved(o)) { - _saved_mark_stack->push(mark); - _saved_oop_stack->push(o); - } - - // mark the object - o->set_mark(markOopDesc::prototype()->set_marked()); -} - -// return true if object is marked -inline bool ObjectMarker::visited(oop o) { - return o->mark()->is_marked(); -} - -// Stack allocated class to help ensure that ObjectMarker is used -// correctly. Constructor initializes ObjectMarker, destructor calls -// ObjectMarker's done() function to restore object headers. -class ObjectMarkerController : public StackObj { - public: - ObjectMarkerController() { - ObjectMarker::init(); - } - ~ObjectMarkerController() { - ObjectMarker::done(); - } -}; - - // helper to map a jvmtiHeapReferenceKind to an old style jvmtiHeapRootKind // (not performance critical as only used for roots) static jvmtiHeapRootKind toJvmtiHeapRootKind(jvmtiHeapReferenceKind kind) { @@ -1894,6 +1762,7 @@ class CallbackInvoker : AllStatic { static JvmtiTagMap* _tag_map; static const void* _user_data; static GrowableArray* _visit_stack; + static JVMTIBitSet* _bitset; // accessors static JvmtiTagMap* tag_map() { return _tag_map; } @@ -1903,7 +1772,7 @@ class CallbackInvoker : AllStatic { // if the object hasn't been visited then push it onto the visit stack // so that it will be visited later static inline bool check_for_visit(oop obj) { - if (!ObjectMarker::visited(obj)) visit_stack()->push(obj); + if (!_bitset->is_marked(obj)) visit_stack()->push(obj); return true; } @@ -1934,13 +1803,13 @@ class CallbackInvoker : AllStatic { static void initialize_for_basic_heap_walk(JvmtiTagMap* tag_map, GrowableArray* visit_stack, const void* user_data, - BasicHeapWalkContext context); + BasicHeapWalkContext context, JVMTIBitSet* bitset); // initialize for advanced mode static void initialize_for_advanced_heap_walk(JvmtiTagMap* tag_map, GrowableArray* visit_stack, const void* user_data, - AdvancedHeapWalkContext context); + AdvancedHeapWalkContext context, JVMTIBitSet* bitset); // functions to report roots static inline bool report_simple_root(jvmtiHeapReferenceKind kind, oop o); @@ -1973,31 +1842,36 @@ AdvancedHeapWalkContext CallbackInvoker::_advanced_context; JvmtiTagMap* CallbackInvoker::_tag_map; const void* CallbackInvoker::_user_data; GrowableArray* CallbackInvoker::_visit_stack; +JVMTIBitSet* CallbackInvoker::_bitset; // initialize for basic heap walk (IterateOverReachableObjects et al) void CallbackInvoker::initialize_for_basic_heap_walk(JvmtiTagMap* tag_map, GrowableArray* visit_stack, const void* user_data, - BasicHeapWalkContext context) { + BasicHeapWalkContext context, + JVMTIBitSet* bitset) { _tag_map = tag_map; _visit_stack = visit_stack; _user_data = user_data; _basic_context = context; _advanced_context.invalidate(); // will trigger assertion if used _heap_walk_type = basic; + _bitset = bitset; } // initialize for advanced heap walk (FollowReferences) void CallbackInvoker::initialize_for_advanced_heap_walk(JvmtiTagMap* tag_map, GrowableArray* visit_stack, const void* user_data, - AdvancedHeapWalkContext context) { + AdvancedHeapWalkContext context, + JVMTIBitSet* bitset) { _tag_map = tag_map; _visit_stack = visit_stack; _user_data = user_data; _advanced_context = context; _basic_context.invalidate(); // will trigger assertion if used _heap_walk_type = advanced; + _bitset = bitset; } @@ -2676,6 +2550,7 @@ class VM_HeapWalkOperation: public VM_Operation { JvmtiTagMap* _tag_map; Handle _initial_object; GrowableArray* _visit_stack; // the visit stack + JVMTIBitSet _bitset; bool _collecting_heap_roots; // are we collecting roots bool _following_object_refs; // are we following object references @@ -2746,8 +2621,7 @@ VM_HeapWalkOperation::VM_HeapWalkOperation(JvmtiTagMap* tag_map, _reporting_string_values = false; _visit_stack = create_visit_stack(); - - CallbackInvoker::initialize_for_basic_heap_walk(tag_map, _visit_stack, user_data, callbacks); + CallbackInvoker::initialize_for_basic_heap_walk(tag_map, _visit_stack, user_data, callbacks, &_bitset); } VM_HeapWalkOperation::VM_HeapWalkOperation(JvmtiTagMap* tag_map, @@ -2763,7 +2637,7 @@ VM_HeapWalkOperation::VM_HeapWalkOperation(JvmtiTagMap* tag_map, _reporting_string_values = (callbacks.string_primitive_value_callback() != NULL);; _visit_stack = create_visit_stack(); - CallbackInvoker::initialize_for_advanced_heap_walk(tag_map, _visit_stack, user_data, callbacks); + CallbackInvoker::initialize_for_advanced_heap_walk(tag_map, _visit_stack, user_data, callbacks, &_bitset); } VM_HeapWalkOperation::~VM_HeapWalkOperation() { @@ -3208,8 +3082,8 @@ inline bool VM_HeapWalkOperation::collect_stack_roots() { // bool VM_HeapWalkOperation::visit(oop o) { // mark object as visited - assert(!ObjectMarker::visited(o), "can't visit same object more than once"); - ObjectMarker::mark(o); + assert(!_bitset.is_marked(o), "can't visit same object more than once"); + _bitset.mark_obj(o); // instance if (o->is_instance()) { @@ -3238,27 +3112,17 @@ bool VM_HeapWalkOperation::visit(oop o) { void VM_HeapWalkOperation::doit() { ResourceMark rm; - ObjectMarkerController marker; ClassFieldMapCacheMark cm; assert(visit_stack()->is_empty(), "visit stack must be empty"); // the heap walk starts with an initial object or the heap roots if (initial_object().is_null()) { - // If either collect_stack_roots() or collect_simple_roots() - // returns false at this point, then there are no mark bits - // to reset. - ObjectMarker::set_needs_reset(false); - - // Calling collect_stack_roots() before collect_simple_roots() // can result in a big performance boost for an agent that is // focused on analyzing references in the thread stacks. if (!collect_stack_roots()) return; if (!collect_simple_roots()) return; - - // no early return so enable heap traversal to reset the mark bits - ObjectMarker::set_needs_reset(true); } else { visit_stack()->push(initial_object()()); } @@ -3270,7 +3134,7 @@ void VM_HeapWalkOperation::doit() { // visited or the callback asked to terminate the iteration. while (!visit_stack()->is_empty()) { oop o = visit_stack()->pop(); - if (!ObjectMarker::visited(o)) { + if (!_bitset.is_marked(o)) { if (!visit(o)) { break; } diff --git a/src/hotspot/share/utilities/objectBitSet.hpp b/src/hotspot/share/utilities/objectBitSet.hpp new file mode 100644 index 00000000000..aa884683ea1 --- /dev/null +++ b/src/hotspot/share/utilities/objectBitSet.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014, 2022, 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_UTILITIES_OBJECTBITSET_HPP +#define SHARE_UTILITIES_OBJECTBITSET_HPP + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/bitMap.hpp" +#include "utilities/hashtable.hpp" + +class MemRegion; + +/* + * ObjectBitSet is a sparse bitmap for marking objects in the Java heap. + * It holds one bit per ObjAlignmentInBytes-aligned address. Its underlying backing memory is + * allocated on-demand only, in fragments covering 64M heap ranges. Fragments are never deleted + * during the lifetime of the ObjectBitSet. The underlying memory is allocated from C-Heap. + */ +template +class ObjectBitSet : public CHeapObj { + const static size_t _bitmap_granularity_shift = 26; // 64M + const static size_t _bitmap_granularity_size = (size_t)1 << _bitmap_granularity_shift; + const static size_t _bitmap_granularity_mask = _bitmap_granularity_size - 1; + + class BitMapFragment; + + class BitMapFragmentTable : public BasicHashtable { + class Entry : public BasicHashtableEntry { + public: + uintptr_t _key; + CHeapBitMap* _value; + + Entry* next() { + return (Entry*)BasicHashtableEntry::next(); + } + }; + + protected: + Entry* bucket(int i) const; + + Entry* new_entry(unsigned int hashValue, uintptr_t key, CHeapBitMap* value); + + unsigned hash_segment(uintptr_t key) { + unsigned hash = (unsigned)key; + return hash ^ (hash >> 3); + } + + unsigned hash_to_index(unsigned hash) { + return hash & (BasicHashtable::table_size() - 1); + } + + public: + BitMapFragmentTable(int table_size) : BasicHashtable(table_size, sizeof(Entry)) {} + ~BitMapFragmentTable(); + void add(uintptr_t key, CHeapBitMap* value); + CHeapBitMap** lookup(uintptr_t key); + }; + + CHeapBitMap* get_fragment_bits(uintptr_t addr); + + BitMapFragmentTable _bitmap_fragments; + BitMapFragment* _fragment_list; + CHeapBitMap* _last_fragment_bits; + uintptr_t _last_fragment_granule; + + public: + ObjectBitSet(); + ~ObjectBitSet(); + + BitMap::idx_t addr_to_bit(uintptr_t addr) const; + + void mark_obj(uintptr_t addr); + + void mark_obj(oop obj) { + return mark_obj(cast_from_oop(obj)); + } + + bool is_marked(uintptr_t addr); + + bool is_marked(oop obj) { + return is_marked(cast_from_oop(obj)); + } +}; + +template +class ObjectBitSet::BitMapFragment : public CHeapObj { + CHeapBitMap _bits; + BitMapFragment* _next; + +public: + BitMapFragment(uintptr_t granule, BitMapFragment* next); + + BitMapFragment* next() const { + return _next; + } + + CHeapBitMap* bits() { + return &_bits; + } +}; + +#endif // SHARE_UTILITIES_OBJECTBITSET_HPP diff --git a/src/hotspot/share/utilities/objectBitSet.inline.hpp b/src/hotspot/share/utilities/objectBitSet.inline.hpp new file mode 100644 index 00000000000..30e672f96a0 --- /dev/null +++ b/src/hotspot/share/utilities/objectBitSet.inline.hpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2019, 2022, 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_UTILITIES_OBJECTBITSET_INLINE_HPP +#define SHARE_UTILITIES_OBJECTBITSET_INLINE_HPP + +#include "utilities/objectBitSet.hpp" + +#include "memory/memRegion.hpp" +#include "utilities/bitMap.inline.hpp" +#include "utilities/hashtable.inline.hpp" + +template +ObjectBitSet::BitMapFragment::BitMapFragment(uintptr_t granule, BitMapFragment* next) : + _bits(_bitmap_granularity_size >> LogMinObjAlignmentInBytes, F, true /* clear */), + _next(next) { +} + +template +ObjectBitSet::ObjectBitSet() : + _bitmap_fragments(32), + _fragment_list(NULL), + _last_fragment_bits(NULL), + _last_fragment_granule(UINTPTR_MAX) { +} + +template +ObjectBitSet::~ObjectBitSet() { + BitMapFragment* current = _fragment_list; + while (current != NULL) { + BitMapFragment* next = current->next(); + delete current; + current = next; + } +} + +template +ObjectBitSet::BitMapFragmentTable::~BitMapFragmentTable() { + for (int index = 0; index < BasicHashtable::table_size(); index ++) { + Entry* e = bucket(index); + while (e != NULL) { + Entry* tmp = e; + e = e->next(); + BasicHashtable::free_entry(tmp); + } + } +} + +template +inline typename ObjectBitSet::BitMapFragmentTable::Entry* ObjectBitSet::BitMapFragmentTable::bucket(int i) const { + return (Entry*)BasicHashtable::bucket(i); +} + +template +inline typename ObjectBitSet::BitMapFragmentTable::Entry* + ObjectBitSet::BitMapFragmentTable::new_entry(unsigned int hash, uintptr_t key, CHeapBitMap* value) { + + Entry* entry = (Entry*)BasicHashtable::new_entry(hash); + entry->_key = key; + entry->_value = value; + return entry; +} + +template +inline void ObjectBitSet::BitMapFragmentTable::add(uintptr_t key, CHeapBitMap* value) { + unsigned hash = hash_segment(key); + Entry* entry = new_entry(hash, key, value); + BasicHashtable::add_entry(hash_to_index(hash), entry); +} + +template +inline CHeapBitMap** ObjectBitSet::BitMapFragmentTable::lookup(uintptr_t key) { + unsigned hash = hash_segment(key); + int index = hash_to_index(hash); + for (Entry* e = bucket(index); e != NULL; e = e->next()) { + if (e->hash() == hash && e->_key == key) { + return &(e->_value); + } + } + return NULL; +} + +template +inline BitMap::idx_t ObjectBitSet::addr_to_bit(uintptr_t addr) const { + return (addr & _bitmap_granularity_mask) >> LogMinObjAlignmentInBytes; +} + +template +inline CHeapBitMap* ObjectBitSet::get_fragment_bits(uintptr_t addr) { + uintptr_t granule = addr >> _bitmap_granularity_shift; + if (granule == _last_fragment_granule) { + return _last_fragment_bits; + } + CHeapBitMap* bits = NULL; + + CHeapBitMap** found = _bitmap_fragments.lookup(granule); + if (found != NULL) { + bits = *found; + } else { + BitMapFragment* fragment = new BitMapFragment(granule, _fragment_list); + bits = fragment->bits(); + _fragment_list = fragment; + if (_bitmap_fragments.number_of_entries() * 100 / _bitmap_fragments.table_size() > 25) { + _bitmap_fragments.resize(_bitmap_fragments.table_size() * 2); + } + _bitmap_fragments.add(granule, bits); + } + + _last_fragment_bits = bits; + _last_fragment_granule = granule; + + return bits; +} + +template +inline void ObjectBitSet::mark_obj(uintptr_t addr) { + CHeapBitMap* bits = get_fragment_bits(addr); + const BitMap::idx_t bit = addr_to_bit(addr); + bits->set_bit(bit); +} + +template +inline bool ObjectBitSet::is_marked(uintptr_t addr) { + CHeapBitMap* bits = get_fragment_bits(addr); + const BitMap::idx_t bit = addr_to_bit(addr); + return bits->at(bit); +} + +#endif // SHARE_UTILITIES_OBJECTBITSET_INLINE_HPP diff --git a/test/hotspot/gtest/utilities/test_objectBitSet.cpp b/test/hotspot/gtest/utilities/test_objectBitSet.cpp new file mode 100644 index 00000000000..bc78c8f770e --- /dev/null +++ b/test/hotspot/gtest/utilities/test_objectBitSet.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022, 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 "memory/allocation.hpp" +#include "utilities/objectBitSet.inline.hpp" +#include "unittest.hpp" + +TEST_VM(ObjectBitSet, empty) { + ObjectBitSet obs; + oopDesc obj1; + ASSERT_FALSE(obs.is_marked(&obj1)); +} + +// NOTE: This is a little weird. NULL is not treated any special: ObjectBitSet will happily +// allocate a fragement for the memory range starting at 0 and mark the first bit when passing NULL. +// In the absense of any error handling, I am not sure what would possibly be a reasonable better +// way to do it, though. +TEST_VM(ObjectBitSet, null) { + ObjectBitSet obs; + ASSERT_FALSE(obs.is_marked((oop)NULL)); + obs.mark_obj((oop) NULL); + ASSERT_TRUE(obs.is_marked((oop)NULL)); +} + +TEST_VM(ObjectBitSet, mark_single) { + ObjectBitSet obs; + oopDesc obj1; + ASSERT_FALSE(obs.is_marked(&obj1)); + obs.mark_obj(&obj1); + ASSERT_TRUE(obs.is_marked(&obj1)); +} + +TEST_VM(ObjectBitSet, mark_multi) { + ObjectBitSet obs; + oopDesc obj1; + oopDesc obj2; + ASSERT_FALSE(obs.is_marked(&obj1)); + ASSERT_FALSE(obs.is_marked(&obj2)); + obs.mark_obj(&obj1); + ASSERT_TRUE(obs.is_marked(&obj1)); + ASSERT_FALSE(obs.is_marked(&obj2)); + obs.mark_obj(&obj2); + ASSERT_TRUE(obs.is_marked(&obj1)); + ASSERT_TRUE(obs.is_marked(&obj2)); +}