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/mutexLocker.hpp"
 35 #include "runtime/vmThread.hpp"
 36 #include "services/heapObjectStatistics.hpp"
 37 #include "utilities/copy.hpp"
 38 #include "utilities/globalDefinitions.hpp"
 39 #include "utilities/ostream.hpp"
 40 
 41 HeapObjectStatistics* HeapObjectStatistics::_instance = NULL;
 42 
 43 class HeapObjectStatsObjectClosure : public ObjectClosure {
 44 private:
 45   HeapObjectStatistics* const _stats;
 46 public:
 47   HeapObjectStatsObjectClosure() : _stats(HeapObjectStatistics::instance()) {}
 48   void do_object(oop obj) {
 49     _stats->visit_object(obj);
 50   }
 51 };
 52 
 53 class VM_HeapObjectStatistics : public VM_Operation {
 54 public:
 55   VMOp_Type type() const { return VMOp_HeapObjectStatistics; }
 56   bool doit_prologue() {
 57     Heap_lock->lock();
 58     return true;
 59   }
 60 
 61   void doit_epilogue() {
 62     Heap_lock->unlock();
 63   }
 64 
 65   void doit() {
 66     assert(SafepointSynchronize::is_at_safepoint(), "all threads are stopped");
 67     assert(Heap_lock->is_locked(), "should have the Heap_lock");
 68 
 69     CollectedHeap* heap = Universe::heap();
 70     heap->ensure_parsability(false);
 71 
 72     HeapObjectStatistics* stats = HeapObjectStatistics::instance();
 73     stats->begin_sample();
 74 
 75     HeapObjectStatsObjectClosure cl;
 76     heap->object_iterate(&cl);
 77   }
 78 };
 79 
 80 HeapObjectStatisticsTask::HeapObjectStatisticsTask() : PeriodicTask(HeapObjectStatsSamplingInterval) {}
 81 
 82 void HeapObjectStatisticsTask::task() {
 83   VM_HeapObjectStatistics vmop;
 84   VMThread::execute(&vmop);
 85 }
 86 
 87 void HeapObjectStatistics::initialize() {
 88   assert(_instance == NULL, "Don't init twice");
 89   if (HeapObjectStats) {
 90     _instance = new HeapObjectStatistics();
 91     _instance->start();
 92   }
 93 }
 94 
 95 void HeapObjectStatistics::shutdown() {
 96   if (HeapObjectStats) {
 97     assert(_instance != NULL, "Must be initialized");
 98     LogTarget(Info, heap, stats) lt;
 99     if (lt.is_enabled()) {
100       LogStream ls(lt);
101       ResourceMark rm;
102       _instance->print(&ls);
103     }
104     _instance->stop();
105     delete _instance;
106     _instance = NULL;
107   }
108 }
109 
110 HeapObjectStatistics* HeapObjectStatistics::instance() {
111   assert(_instance != NULL, "Must be initialized");
112   return _instance;
113 }
114 
115 void HeapObjectStatistics::increase_counter(uint64_t& counter, uint64_t val) {
116   uint64_t oldval = counter;
117   uint64_t newval = counter + val;
118   if (newval < oldval) {
119     log_warning(heap, stats)("HeapObjectStats counter overflow: resulting statistics will be useless");
120   }
121   counter = newval;
122 }
123 
124 HeapObjectStatistics::HeapObjectStatistics() :
125   _task(), _num_samples(0), _num_objects(0), _num_ihashed(0), _num_locked(0), _lds(0) { }
126 
127 void HeapObjectStatistics::start() {
128   _task.enroll();
129 }
130 
131 void HeapObjectStatistics::stop() {
132   _task.disenroll();
133 }
134 
135 void HeapObjectStatistics::begin_sample() {
136   _num_samples++;
137 }
138 
139 void HeapObjectStatistics::visit_object(oop obj) {
140   increase_counter(_num_objects);
141   markWord mark = obj->mark();
142   if (!mark.has_no_hash()) {
143     increase_counter(_num_ihashed);
144     if (mark.age() > 0) {
145       increase_counter(_num_ihashed_moved);
146     }
147   }
148   if (mark.is_locked()) {
149     increase_counter(_num_locked);
150   }
151 #ifdef ASSERT
152 #ifdef _LP64
153   if (!mark.has_displaced_mark_helper()) {
154     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()));
155   }
156 #endif
157 #endif
158   increase_counter(_lds, obj->size());
159 }
160 
161 void HeapObjectStatistics::print(outputStream* out) const {
162   if (!HeapObjectStats) {
163     return;
164   }
165   if (_num_samples == 0 || _num_objects == 0) {
166     return;
167   }
168 
169   out->print_cr("Number of samples:  " UINT64_FORMAT, _num_samples);
170   out->print_cr("Average number of objects: " UINT64_FORMAT, _num_objects / _num_samples);
171   out->print_cr("Average object size: " UINT64_FORMAT " bytes, %.1f words", (_lds * HeapWordSize) / _num_objects, (float) _lds / _num_objects);
172   out->print_cr("Average number of hashed objects: " UINT64_FORMAT " (%.2f%%)", _num_ihashed / _num_samples, (float) (_num_ihashed * 100.0) / _num_objects);
173   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);
174   out->print_cr("Average number of locked objects: " UINT64_FORMAT " (%.2f%%)", _num_locked / _num_samples, (float) (_num_locked * 100) / _num_objects);
175   out->print_cr("Average LDS: " UINT64_FORMAT " bytes", _lds * HeapWordSize / _num_samples);
176   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);
177 }