Wed, 09 Oct 2019 16:11:58 +0800
8214542: JFR: Old Object Sample event slow on a deep heap in debug builds
Reviewed-by: egahlin, rwestberg
1 /*
2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. 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 */
25 #include "precompiled.hpp"
26 #include "jfr/jfrEvents.hpp"
27 #include "jfr/recorder/jfrRecorder.hpp"
28 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
29 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
30 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
31 #include "jfr/leakprofiler/chains/edgeStore.hpp"
32 #include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
33 #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
34 #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp"
35 #include "jfr/leakprofiler/leakProfiler.hpp"
36 #include "jfr/leakprofiler/sampling/objectSample.hpp"
37 #include "jfr/leakprofiler/sampling/objectSampler.hpp"
38 #include "jfr/leakprofiler/utilities/rootType.hpp"
39 #include "jfr/metadata/jfrSerializer.hpp"
40 #include "runtime/interfaceSupport.hpp"
41 #include "runtime/mutexLocker.hpp"
42 #include "runtime/thread.inline.hpp"
44 template <typename SampleProcessor>
45 static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) {
46 assert(sample != NULL, "invariant");
47 while (sample != end) {
48 processor.sample_do(sample);
49 sample = sample->next();
50 }
51 }
53 class RootSystemType : public JfrSerializer {
54 public:
55 void serialize(JfrCheckpointWriter& writer) {
56 const u4 nof_root_systems = OldObjectRoot::_number_of_systems;
57 writer.write_count(nof_root_systems);
58 for (u4 i = 0; i < nof_root_systems; ++i) {
59 writer.write_key(i);
60 writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i));
61 }
62 }
63 };
65 class RootType : public JfrSerializer {
66 public:
67 void serialize(JfrCheckpointWriter& writer) {
68 const u4 nof_root_types = OldObjectRoot::_number_of_types;
69 writer.write_count(nof_root_types);
70 for (u4 i = 0; i < nof_root_types; ++i) {
71 writer.write_key(i);
72 writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i));
73 }
74 }
75 };
77 class CheckpointInstall {
78 private:
79 const JfrCheckpointBlobHandle& _cp;
80 public:
81 CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {}
82 void sample_do(ObjectSample* sample) {
83 assert(sample != NULL, "invariant");
84 if (!sample->is_dead()) {
85 sample->set_klass_checkpoint(_cp);
86 }
87 }
88 };
90 class CheckpointWrite {
91 private:
92 JfrCheckpointWriter& _writer;
93 const jlong _last_sweep;
94 public:
95 CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {}
96 void sample_do(ObjectSample* sample) {
97 assert(sample != NULL, "invariant");
98 if (sample->is_alive_and_older_than(_last_sweep)) {
99 if (sample->has_thread_checkpoint()) {
100 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
101 thread_cp->exclusive_write(_writer);
102 }
103 if (sample->has_klass_checkpoint()) {
104 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
105 klass_cp->exclusive_write(_writer);
106 }
107 }
108 }
109 };
111 class CheckpointStateReset {
112 private:
113 const jlong _last_sweep;
114 public:
115 CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {}
116 void sample_do(ObjectSample* sample) {
117 assert(sample != NULL, "invariant");
118 if (sample->is_alive_and_older_than(_last_sweep)) {
119 if (sample->has_thread_checkpoint()) {
120 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
121 thread_cp->reset_write_state();
122 }
123 if (sample->has_klass_checkpoint()) {
124 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
125 klass_cp->reset_write_state();
126 }
127 }
128 }
129 };
131 class StackTraceWrite {
132 private:
133 JfrStackTraceRepository& _stack_trace_repo;
134 JfrCheckpointWriter& _writer;
135 int _count;
136 public:
137 StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) :
138 _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) {
139 JfrStacktrace_lock->lock();
140 }
141 ~StackTraceWrite() {
142 assert(JfrStacktrace_lock->owned_by_self(), "invariant");
143 JfrStacktrace_lock->unlock();
144 }
146 void sample_do(ObjectSample* sample) {
147 assert(sample != NULL, "invariant");
148 if (!sample->is_dead()) {
149 if (sample->has_stack_trace()) {
150 JfrTraceId::use(sample->klass(), true);
151 _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash());
152 ++_count;
153 }
154 }
155 }
157 int count() const {
158 return _count;
159 }
160 };
162 class SampleMark {
163 private:
164 ObjectSampleMarker& _marker;
165 jlong _last_sweep;
166 int _count;
167 public:
168 SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker),
169 _last_sweep(last_sweep),
170 _count(0) {}
171 void sample_do(ObjectSample* sample) {
172 assert(sample != NULL, "invariant");
173 if (sample->is_alive_and_older_than(_last_sweep)) {
174 _marker.mark(sample->object());
175 ++_count;
176 }
177 }
179 int count() const {
180 return _count;
181 }
182 };
184 void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool type_set) {
185 if (!writer.has_data()) {
186 return;
187 }
189 assert(writer.has_data(), "invariant");
190 const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob();
191 CheckpointInstall install(h_cp);
193 // Class unload implies a safepoint.
194 // Not class unload implies the object sampler is locked, because it was claimed exclusively earlier.
195 // Therefore: direct access the object sampler instance is safe.
196 ObjectSampler* const object_sampler = ObjectSampler::sampler();
197 assert(object_sampler != NULL, "invariant");
199 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
200 const ObjectSample* const last_resolved = object_sampler->last_resolved();
202 // install only to new samples since last resolved checkpoint
203 if (last != last_resolved) {
204 do_samples(last, last_resolved, install);
205 if (class_unload) {
206 return;
207 }
208 if (type_set) {
209 object_sampler->set_last_resolved(last);
210 }
211 }
212 }
214 void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
215 assert(sampler != NULL, "invariant");
216 assert(edge_store != NULL, "invariant");
217 assert(thread != NULL, "invariant");
219 static bool types_registered = false;
220 if (!types_registered) {
221 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
222 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
223 types_registered = true;
224 }
226 const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
227 ObjectSample* const last = const_cast<ObjectSample*>(sampler->last());
228 {
229 JfrCheckpointWriter writer(false, false, thread);
230 CheckpointWrite checkpoint_write(writer, last_sweep);
231 do_samples(last, NULL, checkpoint_write);
232 }
234 CheckpointStateReset state_reset(last_sweep);
235 do_samples(last, NULL, state_reset);
237 if (!edge_store->is_empty()) {
238 // java object and chain representations
239 JfrCheckpointWriter writer(false, true, thread);
240 ObjectSampleWriter osw(writer, edge_store);
241 edge_store->iterate(osw);
242 }
243 }
245 int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) {
246 assert(object_sampler != NULL, "invariant");
247 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
248 if (last == NULL) {
249 return 0;
250 }
251 const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
252 SampleMark mark(marker, last_sweep);
253 do_samples(last, NULL, mark);
254 return mark.count();
255 }
257 WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) :
258 _sampler(sampler), _stack_trace_repo(repo) {}
260 bool WriteObjectSampleStacktrace::process() {
261 assert(LeakProfiler::is_running(), "invariant");
262 assert(_sampler != NULL, "invariant");
264 ObjectSample* const last = const_cast<ObjectSample*>(_sampler->last());
265 const ObjectSample* const last_resolved = _sampler->last_resolved();
266 if (last == last_resolved) {
267 return true;
268 }
270 JfrCheckpointWriter writer(false, true, Thread::current());
271 const JfrCheckpointContext ctx = writer.context();
273 writer.write_type(TYPE_STACKTRACE);
274 const jlong count_offset = writer.reserve(sizeof(u4));
276 int count = 0;
277 {
278 StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock
279 do_samples(last, last_resolved, stack_trace_write);
280 count = stack_trace_write.count();
281 }
282 if (count == 0) {
283 writer.set_context(ctx);
284 return true;
285 }
286 assert(count > 0, "invariant");
287 writer.write_count((u4)count, count_offset);
288 JfrStackTraceRepository::write_metadata(writer);
290 // install the stacktrace checkpoint information to the candidates
291 ObjectSampleCheckpoint::install(writer, false, false);
292 return true;
293 }