1 /*
  2  * Copyright (c) 2021, Red Hat, Inc. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "precompiled.hpp"
 26 #include "gc/shared/collectedHeap.hpp"
 27 #include "logging/logStream.hpp"
 28 #include "logging/logTag.hpp"
 29 #include "memory/allocation.hpp"
 30 #include "memory/iterator.hpp"
 31 #include "memory/resourceArea.hpp"
 32 #include "memory/universe.hpp"
 33 #include "oops/oop.inline.hpp"
 34 #include "runtime/vmThread.hpp"
 35 #include "services/heapObjectStatistics.hpp"
 36 #include "utilities/copy.hpp"
 37 #include "utilities/globalDefinitions.hpp"
 38 #include "utilities/ostream.hpp"
 39 
 40 HeapObjectStatistics* HeapObjectStatistics::_instance = NULL;
 41 
 42 class HeapObjectStatsObjectClosure : public ObjectClosure {
 43 private:
 44   HeapObjectStatistics* const _stats;
 45 public:
 46   HeapObjectStatsObjectClosure() : _stats(HeapObjectStatistics::instance()) {}
 47   void do_object(oop obj) {
 48     _stats->visit_object(obj);
 49   }
 50 };
 51 
 52 class VM_HeapObjectStatistics : public VM_Operation {
 53 public:
 54   VMOp_Type type() const { return VMOp_HeapObjectStatistics; }
 55   bool doit_prologue() {
 56     Heap_lock->lock();
 57     return true;
 58   }
 59 
 60   void doit_epilogue() {
 61     Heap_lock->unlock();
 62   }
 63 
 64   void doit() {
 65     assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped");
 66     assert(Heap_lock->is_locked(), "should have the Heap_lock");
 67 
 68     CollectedHeap* heap = Universe::heap();
 69     heap->ensure_parsability(false);
 70 
 71     HeapObjectStatistics* stats = HeapObjectStatistics::instance();
 72     stats->begin_sample();
 73 
 74     HeapObjectStatsObjectClosure cl;
 75     heap->object_iterate(&cl);
 76   }
 77 };
 78 
 79 HeapObjectStatisticsTask::HeapObjectStatisticsTask() : PeriodicTask(HeapObjectStatsSamplingInterval) {}
 80 
 81 void HeapObjectStatisticsTask::task() {
 82   VM_HeapObjectStatistics vmop;
 83   VMThread::execute(&vmop);
 84 }
 85 
 86 void HeapObjectStatistics::initialize() {
 87   assert(_instance == NULL, "Don't init twice");
 88   if (HeapObjectStats) {
 89     _instance = new HeapObjectStatistics();
 90     _instance->start();
 91   }
 92 }
 93 
 94 void HeapObjectStatistics::shutdown() {
 95   if (HeapObjectStats) {
 96     assert(_instance != NULL, "Must be initialized");
 97     LogTarget(Info, heap, stats) lt;
 98     if (lt.is_enabled()) {
 99       LogStream ls(lt);
100       ResourceMark rm;
101       _instance->print(&ls);
102     }
103     _instance->stop();
104     delete _instance;
105     _instance = NULL;
106   }
107 }
108 
109 HeapObjectStatistics* HeapObjectStatistics::instance() {
110   assert(_instance != NULL, "Must be initialized");
111   return _instance;
112 }
113 
114 void HeapObjectStatistics::increase_counter(uint64_t& counter, uint64_t val) {
115   uint64_t oldval = counter;
116   uint64_t newval = counter + val;
117   if (newval < oldval) {
118     log_warning(heap, stats)("HeapObjectStats counter overflow: resulting statistics will be useless");
119   }
120   counter = newval;
121 }
122 
123 HeapObjectStatistics::HeapObjectStatistics() :
124   _task(), _num_samples(0), _num_objects(0), _num_ihashed(0), _num_locked(0), _lds(0) { }
125 
126 void HeapObjectStatistics::start() {
127   _task.enroll();
128 }
129 
130 void HeapObjectStatistics::stop() {
131   _task.disenroll();
132 }
133 
134 void HeapObjectStatistics::begin_sample() {
135   _num_samples++;
136 }
137 
138 void HeapObjectStatistics::visit_object(oop obj) {
139   increase_counter(_num_objects);
140   markWord mark = obj->mark();
141   if (!mark.has_no_hash()) {
142     increase_counter(_num_ihashed);
143     if (mark.age() > 0) {
144       increase_counter(_num_ihashed_moved);
145     }
146   }
147   if (mark.is_locked()) {
148     increase_counter(_num_locked);
149   }
150 #ifdef ASSERT
151 #ifdef _LP64
152   if (!mark.has_displaced_mark_helper()) {
153     assert(mark.narrow_klass() == CompressedKlassPointers::encode(obj->klass_or_null()), "upper 32 mark bits must be narrow klass: mark: " INTPTR_FORMAT ", compressed-klass: " INTPTR_FORMAT, (intptr_t)mark.narrow_klass(), (intptr_t)CompressedKlassPointers::encode(obj->klass_or_null()));
154   }
155 #endif
156 #endif
157   increase_counter(_lds, obj->size());
158 }
159 
160 void HeapObjectStatistics::print(outputStream* out) const {
161   if (!HeapObjectStats) {
162     return;
163   }
164   if (_num_samples == 0 || _num_objects == 0) {
165     return;
166   }
167 
168   out->print_cr("Number of samples:  " UINT64_FORMAT, _num_samples);
169   out->print_cr("Average number of objects: " UINT64_FORMAT, _num_objects / _num_samples);
170   out->print_cr("Average object size: " UINT64_FORMAT " bytes, %.1f words", (_lds * HeapWordSize) / _num_objects, (float) _lds / _num_objects);
171   out->print_cr("Average number of hashed objects: " UINT64_FORMAT " (%.2f%%)", _num_ihashed / _num_samples, (float) (_num_ihashed * 100.0) / _num_objects);
172   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);
173   out->print_cr("Average number of locked objects: " UINT64_FORMAT " (%.2f%%)", _num_locked / _num_samples, (float) (_num_locked * 100) / _num_objects);
174   out->print_cr("Average LDS: " UINT64_FORMAT " bytes", _lds * HeapWordSize / _num_samples);
175   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);
176 }