diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 3af579aed03..7e460bc2c3d 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -130,7 +130,7 @@ class markWord { static const int lock_bits = 2; static const int biased_lock_bits = 1; static const int max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits; - static const int hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits; + static const int hash_bits = max_hash_bits > 22 ? 22 : max_hash_bits; static const int unused_gap_bits = LP64_ONLY(1) NOT_LP64(0); static const int epoch_bits = 2; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 31ad1619880..97a5dbc1466 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2048,6 +2048,12 @@ const intx ObjectAlignmentInBytes = 8; false AARCH64_ONLY(DEBUG_ONLY(||true)), \ "Mark all threads after a safepoint, and clear on a modify " \ "fence. Add cleanliness checks.") \ + \ + product(bool, HeapObjectStats, false, DIAGNOSTIC, \ + "Enable gathering of heap object statistics") \ + \ + product(size_t, HeapObjectStatsSamplingInterval, 500, DIAGNOSTIC, \ + "Heap object statistics sampling interval (ms)") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 27c46f92b80..fdd32b073cf 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -116,6 +116,7 @@ #include "runtime/vmOperations.hpp" #include "runtime/vm_version.hpp" #include "services/attachListener.hpp" +#include "services/heapObjectStatistics.hpp" #include "services/management.hpp" #include "services/memTracker.hpp" #include "services/threadService.hpp" @@ -2956,6 +2957,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // Start the monitor deflation thread: MonitorDeflationThread::initialize(); + // Start heap object statistics sampling + HeapObjectStatistics::initialize(); + // initialize compiler(s) #if defined(COMPILER1) || COMPILER2_OR_JVMCI #if INCLUDE_JVMCI @@ -3404,6 +3408,8 @@ void Threads::destroy_vm() { // complete it is safe to directly delete the thread at any time. ThreadsSMRSupport::wait_until_not_protected(thread); + HeapObjectStatistics::shutdown(); + // Stop VM thread. { // 4945125 The vm thread comes to a safepoint during exit. diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index 1fe9a60841e..20e240831a7 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -81,6 +81,7 @@ template(ChangeSingleStep) \ template(HeapWalkOperation) \ template(HeapIterateOperation) \ + template(HeapObjectStatistics) \ template(ReportJavaOutOfMemory) \ template(JFRCheckpoint) \ template(ShenandoahFullGC) \ diff --git a/src/hotspot/share/services/heapObjectStatistics.cpp b/src/hotspot/share/services/heapObjectStatistics.cpp new file mode 100644 index 00000000000..6cf0423271a --- /dev/null +++ b/src/hotspot/share/services/heapObjectStatistics.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. 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 "gc/shared/collectedHeap.hpp" +#include "logging/logStream.hpp" +#include "logging/logTag.hpp" +#include "memory/allocation.hpp" +#include "memory/iterator.hpp" +#include "memory/resourceArea.hpp" +#include "memory/universe.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/vmThread.hpp" +#include "services/heapObjectStatistics.hpp" +#include "utilities/copy.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +HeapObjectStatistics* HeapObjectStatistics::_instance = NULL; + +class HeapObjectStatsObjectClosure : public ObjectClosure { +private: + HeapObjectStatistics* const _stats; +public: + HeapObjectStatsObjectClosure() : _stats(HeapObjectStatistics::instance()) {} + void do_object(oop obj) { + _stats->visit_object(obj); + } +}; + +class VM_HeapObjectStatistics : public VM_Operation { +public: + VMOp_Type type() const { return VMOp_HeapObjectStatistics; } + bool doit_prologue() { + Heap_lock->lock(); + return true; + } + + void doit_epilogue() { + Heap_lock->unlock(); + } + + void doit() { + assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped"); + assert(Heap_lock->is_locked(), "should have the Heap_lock"); + + CollectedHeap* heap = Universe::heap(); + heap->ensure_parsability(false); + + HeapObjectStatistics* stats = HeapObjectStatistics::instance(); + stats->begin_sample(); + + HeapObjectStatsObjectClosure cl; + heap->object_iterate(&cl); + } +}; + +HeapObjectStatisticsTask::HeapObjectStatisticsTask() : PeriodicTask(HeapObjectStatsSamplingInterval) {} + +void HeapObjectStatisticsTask::task() { + VM_HeapObjectStatistics vmop; + VMThread::execute(&vmop); +} + +void HeapObjectStatistics::initialize() { + assert(_instance == NULL, "Don't init twice"); + if (HeapObjectStats) { + _instance = new HeapObjectStatistics(); + _instance->start(); + } +} + +void HeapObjectStatistics::shutdown() { + if (HeapObjectStats) { + assert(_instance != NULL, "Must be initialized"); + LogTarget(Info, heap, stats) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ResourceMark rm; + _instance->print(&ls); + } + _instance->stop(); + delete _instance; + _instance = NULL; + } +} + +HeapObjectStatistics* HeapObjectStatistics::instance() { + assert(_instance != NULL, "Must be initialized"); + return _instance; +} + +void HeapObjectStatistics::increase_counter(uint64_t& counter, uint64_t val) { + uint64_t oldval = counter; + uint64_t newval = counter + val; + if (newval < oldval) { + log_warning(heap, stats)("HeapObjectStats counter overflow: resulting statistics will be useless"); + } + counter = newval; +} + +HeapObjectStatistics::HeapObjectStatistics() : + _task(), _num_samples(0), _num_objects(0), _num_ihashed(0), _num_locked(0), _lds(0) { } + +void HeapObjectStatistics::start() { + _task.enroll(); +} + +void HeapObjectStatistics::stop() { + _task.disenroll(); +} + +void HeapObjectStatistics::begin_sample() { + _num_samples++; +} + +void HeapObjectStatistics::visit_object(oop obj) { + increase_counter(_num_objects); + if (!obj->mark().has_no_hash()) { + increase_counter(_num_ihashed); + if (obj->mark().age() > 0) { + increase_counter(_num_ihashed_moved); + } + } + if (obj->mark().is_locked()) { + increase_counter(_num_locked); + } + increase_counter(_lds, obj->size()); +} + +void HeapObjectStatistics::print(outputStream* out) const { + if (!HeapObjectStats) { + return; + } + if (_num_samples == 0 || _num_objects == 0) { + return; + } + + out->print_cr("Number of samples: " UINT64_FORMAT, _num_samples); + out->print_cr("Average number of objects: " UINT64_FORMAT, _num_objects / _num_samples); + out->print_cr("Average object size: " UINT64_FORMAT " bytes, %.1f words", (_lds * HeapWordSize) / _num_objects, (float) _lds / _num_objects); + out->print_cr("Average number of hashed objects: " UINT64_FORMAT " (%.2f%%)", _num_ihashed / _num_samples, (float) (_num_ihashed * 100.0) / _num_objects); + out->print_cr("Average number of moved hashed objects: " UINT64_FORMAT " (%.2f%%)", _num_ihashed_moved / _num_samples, (float) (_num_ihashed_moved * 100.0) / _num_objects); + out->print_cr("Average number of locked objects: " UINT64_FORMAT " (%.2f%%)", _num_locked / _num_samples, (float) (_num_locked * 100) / _num_objects); + out->print_cr("Average LDS: " UINT64_FORMAT " bytes", _lds * HeapWordSize / _num_samples); + out->print_cr("Avg LDS with (assumed) 64bit header: " UINT64_FORMAT " bytes (%.1f%%)", (_lds - _num_objects) * HeapWordSize / _num_samples, ((float) _lds - _num_objects) * 100.0 / _lds); +} diff --git a/src/hotspot/share/services/heapObjectStatistics.hpp b/src/hotspot/share/services/heapObjectStatistics.hpp new file mode 100644 index 00000000000..04fdf824263 --- /dev/null +++ b/src/hotspot/share/services/heapObjectStatistics.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. 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_SERVICES_HEAPOBJECTSTATISTICS_HPP +#define SHARE_SERVICES_HEAPOBJECTSTATISTICS_HPP + +#include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" +#include "runtime/task.hpp" +#include "runtime/vmOperation.hpp" + +class outputStream; + +class HeapObjectStatisticsTask : public PeriodicTask { +public: + HeapObjectStatisticsTask(); + void task(); +}; + +class HeapObjectStatistics : public CHeapObj { +private: + static HeapObjectStatistics* _instance; + + HeapObjectStatisticsTask _task; + uint64_t _num_samples; + uint64_t _num_objects; + uint64_t _num_ihashed; + uint64_t _num_ihashed_moved; + uint64_t _num_locked; + uint64_t _lds; + + static void increase_counter(uint64_t& counter, uint64_t val = 1); + + void print(outputStream* out) const; + +public: + static void initialize(); + static void shutdown(); + + static HeapObjectStatistics* instance(); + + HeapObjectStatistics(); + void start(); + void stop(); + + void begin_sample(); + void visit_object(oop object); +}; + +#endif // SHARE_SERVICES_HEAPOBJECTSTATISTICS_HPP