32 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
32 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
33 #include "jfr/support/jfrThreadLocal.hpp" |
33 #include "jfr/support/jfrThreadLocal.hpp" |
34 #include "jfr/utilities/jfrTryLock.hpp" |
34 #include "jfr/utilities/jfrTryLock.hpp" |
35 #include "memory/universe.hpp" |
35 #include "memory/universe.hpp" |
36 #include "oops/oop.inline.hpp" |
36 #include "oops/oop.inline.hpp" |
|
37 #include "runtime/atomic.hpp" |
|
38 #include "runtime/orderAccess.hpp" |
|
39 #include "runtime/safepoint.hpp" |
37 #include "runtime/thread.hpp" |
40 #include "runtime/thread.hpp" |
|
41 |
|
42 static ObjectSampler* _instance = NULL; |
|
43 |
|
44 static ObjectSampler& instance() { |
|
45 assert(_instance != NULL, "invariant"); |
|
46 return *_instance; |
|
47 } |
38 |
48 |
39 ObjectSampler::ObjectSampler(size_t size) : |
49 ObjectSampler::ObjectSampler(size_t size) : |
40 _priority_queue(new SamplePriorityQueue(size)), |
50 _priority_queue(new SamplePriorityQueue(size)), |
41 _list(new SampleList(size)), |
51 _list(new SampleList(size)), |
42 _last_sweep(JfrTicks::now()), |
52 _last_sweep(JfrTicks::now()), |
43 _total_allocated(0), |
53 _total_allocated(0), |
44 _threshold(0), |
54 _threshold(0), |
45 _size(size), |
55 _size(size), |
46 _tryLock(0), |
|
47 _dead_samples(false) {} |
56 _dead_samples(false) {} |
48 |
57 |
49 ObjectSampler::~ObjectSampler() { |
58 ObjectSampler::~ObjectSampler() { |
50 delete _priority_queue; |
59 delete _priority_queue; |
51 _priority_queue = NULL; |
60 _priority_queue = NULL; |
52 delete _list; |
61 delete _list; |
53 _list = NULL; |
62 _list = NULL; |
54 } |
63 } |
55 |
64 |
56 void ObjectSampler::add(HeapWord* obj, size_t allocated, JavaThread* thread) { |
65 bool ObjectSampler::create(size_t size) { |
57 assert(thread != NULL, "invariant"); |
66 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
58 const traceid thread_id = thread->threadObj() != NULL ? thread->jfr_thread_local()->thread_id() : 0; |
67 assert(_instance == NULL, "invariant"); |
|
68 _instance = new ObjectSampler(size); |
|
69 return _instance != NULL; |
|
70 } |
|
71 |
|
72 bool ObjectSampler::is_created() { |
|
73 return _instance != NULL; |
|
74 } |
|
75 |
|
76 ObjectSampler* ObjectSampler::sampler() { |
|
77 assert(is_created(), "invariant"); |
|
78 return _instance; |
|
79 } |
|
80 |
|
81 void ObjectSampler::destroy() { |
|
82 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
|
83 if (_instance != NULL) { |
|
84 ObjectSampler* const sampler = _instance; |
|
85 _instance = NULL; |
|
86 delete sampler; |
|
87 } |
|
88 } |
|
89 |
|
90 static volatile int _lock = 0; |
|
91 |
|
92 ObjectSampler* ObjectSampler::acquire() { |
|
93 assert(is_created(), "invariant"); |
|
94 while (Atomic::cmpxchg(1, &_lock, 0) == 1) {} |
|
95 return _instance; |
|
96 } |
|
97 |
|
98 void ObjectSampler::release() { |
|
99 assert(is_created(), "invariant"); |
|
100 OrderAccess::fence(); |
|
101 _lock = 0; |
|
102 } |
|
103 |
|
104 static traceid get_thread_id(JavaThread* thread) { |
|
105 assert(thread != NULL, "invariant"); |
|
106 if (thread->threadObj() == NULL) { |
|
107 return 0; |
|
108 } |
|
109 const JfrThreadLocal* const tl = thread->jfr_thread_local(); |
|
110 assert(tl != NULL, "invariant"); |
|
111 if (!tl->has_thread_checkpoint()) { |
|
112 JfrCheckpointManager::create_thread_checkpoint(thread); |
|
113 } |
|
114 assert(tl->has_thread_checkpoint(), "invariant"); |
|
115 return tl->thread_id(); |
|
116 } |
|
117 |
|
118 // Populates the thread local stack frames, but does not add them |
|
119 // to the stacktrace repository (...yet, see stacktrace_id() below) |
|
120 // |
|
121 void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) { |
|
122 assert(stacktrace != NULL, "invariant"); |
|
123 assert(thread != NULL, "invariant"); |
|
124 if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { |
|
125 JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0); |
|
126 } |
|
127 } |
|
128 |
|
129 // We were successful in acquiring the try lock and have been selected for adding a sample. |
|
130 // Go ahead with installing our previously taken stacktrace into the stacktrace repository. |
|
131 // |
|
132 traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { |
|
133 assert(stacktrace != NULL, "invariant"); |
|
134 assert(stacktrace->hash() != 0, "invariant"); |
|
135 const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread); |
|
136 thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); |
|
137 return stacktrace_id; |
|
138 } |
|
139 |
|
140 void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { |
|
141 assert(thread != NULL, "invariant"); |
|
142 assert(is_created(), "invariant"); |
|
143 |
|
144 const traceid thread_id = get_thread_id(thread); |
59 if (thread_id == 0) { |
145 if (thread_id == 0) { |
60 return; |
146 return; |
61 } |
147 } |
62 assert(thread_id != 0, "invariant"); |
148 const JfrThreadLocal* const tl = thread->jfr_thread_local(); |
63 |
149 JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); |
64 if (!thread->jfr_thread_local()->has_thread_checkpoint()) { |
150 fill_stacktrace(&stacktrace, thread); |
65 JfrCheckpointManager::create_thread_checkpoint(thread); |
151 |
66 assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); |
152 // try enter critical section |
67 } |
153 JfrTryLock tryLock(&_lock); |
68 |
|
69 traceid stack_trace_id = 0; |
|
70 unsigned int stack_trace_hash = 0; |
|
71 if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { |
|
72 stack_trace_id = JfrStackTraceRepository::record(thread, 0, &stack_trace_hash); |
|
73 thread->jfr_thread_local()->set_cached_stack_trace_id(stack_trace_id, stack_trace_hash); |
|
74 } |
|
75 |
|
76 JfrTryLock tryLock(&_tryLock); |
|
77 if (!tryLock.has_lock()) { |
154 if (!tryLock.has_lock()) { |
78 if (LogJFR && Verbose) tty->print_cr("Skipping old object sample due to lock contention"); |
155 if (LogJFR && Verbose) tty->print_cr("Skipping old object sample due to lock contention"); |
79 return; |
156 return; |
80 } |
157 } |
|
158 |
|
159 instance().add(obj, allocated, thread_id, &stacktrace, thread); |
|
160 } |
|
161 |
|
162 void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) { |
|
163 assert(stacktrace != NULL, "invariant"); |
|
164 assert(thread_id != 0, "invariant"); |
|
165 assert(thread != NULL, "invariant"); |
|
166 assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); |
81 |
167 |
82 if (_dead_samples) { |
168 if (_dead_samples) { |
83 scavenge(); |
169 scavenge(); |
84 assert(!_dead_samples, "invariant"); |
170 assert(!_dead_samples, "invariant"); |
85 } |
171 } |
98 } else { |
184 } else { |
99 sample = _list->get(); |
185 sample = _list->get(); |
100 } |
186 } |
101 |
187 |
102 assert(sample != NULL, "invariant"); |
188 assert(sample != NULL, "invariant"); |
103 assert(thread_id != 0, "invariant"); |
|
104 sample->set_thread_id(thread_id); |
189 sample->set_thread_id(thread_id); |
105 sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); |
190 sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); |
106 |
191 |
107 if (stack_trace_id != 0) { |
192 const unsigned int stacktrace_hash = stacktrace->hash(); |
108 sample->set_stack_trace_id(stack_trace_id); |
193 if (stacktrace_hash != 0) { |
109 sample->set_stack_trace_hash(stack_trace_hash); |
194 sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); |
|
195 sample->set_stack_trace_hash(stacktrace_hash); |
110 } |
196 } |
111 |
197 |
112 sample->set_span(allocated); |
198 sample->set_span(allocated); |
113 sample->set_object((oop)obj); |
199 sample->set_object((oop)obj); |
114 sample->set_allocated(allocated); |
200 sample->set_allocated(allocated); |
115 sample->set_allocation_time(JfrTicks::now()); |
201 sample->set_allocation_time(JfrTicks::now()); |
116 sample->set_heap_used_at_last_gc(Universe::get_heap_used_at_last_gc()); |
202 sample->set_heap_used_at_last_gc(Universe::get_heap_used_at_last_gc()); |
117 _priority_queue->push(sample); |
203 _priority_queue->push(sample); |
118 } |
204 } |
119 |
205 |
120 const ObjectSample* ObjectSampler::last() const { |
206 void ObjectSampler::scavenge() { |
121 return _list->last(); |
|
122 } |
|
123 |
|
124 const ObjectSample* ObjectSampler::first() const { |
|
125 return _list->first(); |
|
126 } |
|
127 |
|
128 const ObjectSample* ObjectSampler::last_resolved() const { |
|
129 return _list->last_resolved(); |
|
130 } |
|
131 |
|
132 void ObjectSampler::set_last_resolved(const ObjectSample* sample) { |
|
133 _list->set_last_resolved(sample); |
|
134 } |
|
135 |
|
136 void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { |
|
137 ObjectSample* current = _list->last(); |
207 ObjectSample* current = _list->last(); |
138 while (current != NULL) { |
208 while (current != NULL) { |
139 ObjectSample* next = current->next(); |
209 ObjectSample* next = current->next(); |
140 if (!current->is_dead()) { |
210 if (current->is_dead()) { |
141 if (is_alive->do_object_b(current->object())) { |
211 remove_dead(current); |
142 // The weakly referenced object is alive, update pointer |
|
143 f->do_oop(const_cast<oop*>(current->object_addr())); |
|
144 } else { |
|
145 current->set_dead(); |
|
146 _dead_samples = true; |
|
147 } |
|
148 } |
212 } |
149 current = next; |
213 current = next; |
150 } |
214 } |
151 _last_sweep = JfrTicks::now(); |
215 _dead_samples = false; |
152 } |
216 } |
153 |
217 |
154 void ObjectSampler::remove_dead(ObjectSample* sample) { |
218 void ObjectSampler::remove_dead(ObjectSample* sample) { |
155 assert(sample != NULL, "invariant"); |
219 assert(sample != NULL, "invariant"); |
156 assert(sample->is_dead(), "invariant"); |
220 assert(sample->is_dead(), "invariant"); |
163 } |
227 } |
164 _priority_queue->remove(sample); |
228 _priority_queue->remove(sample); |
165 _list->release(sample); |
229 _list->release(sample); |
166 } |
230 } |
167 |
231 |
168 void ObjectSampler::scavenge() { |
232 void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { |
169 ObjectSample* current = _list->last(); |
233 assert(is_created(), "invariant"); |
|
234 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
|
235 ObjectSampler& sampler = instance(); |
|
236 ObjectSample* current = sampler._list->last(); |
170 while (current != NULL) { |
237 while (current != NULL) { |
171 ObjectSample* next = current->next(); |
238 ObjectSample* next = current->next(); |
172 if (current->is_dead()) { |
239 if (!current->is_dead()) { |
173 remove_dead(current); |
240 if (is_alive->do_object_b(current->object())) { |
|
241 // The weakly referenced object is alive, update pointer |
|
242 f->do_oop(const_cast<oop*>(current->object_addr())); |
|
243 } else { |
|
244 current->set_dead(); |
|
245 sampler._dead_samples = true; |
|
246 } |
174 } |
247 } |
175 current = next; |
248 current = next; |
176 } |
249 } |
177 _dead_samples = false; |
250 sampler._last_sweep = JfrTicks::now(); |
|
251 } |
|
252 |
|
253 const ObjectSample* ObjectSampler::last() const { |
|
254 return _list->last(); |
|
255 } |
|
256 |
|
257 const ObjectSample* ObjectSampler::first() const { |
|
258 return _list->first(); |
|
259 } |
|
260 |
|
261 const ObjectSample* ObjectSampler::last_resolved() const { |
|
262 return _list->last_resolved(); |
|
263 } |
|
264 |
|
265 void ObjectSampler::set_last_resolved(const ObjectSample* sample) { |
|
266 _list->set_last_resolved(sample); |
178 } |
267 } |
179 |
268 |
180 int ObjectSampler::item_count() const { |
269 int ObjectSampler::item_count() const { |
181 return _priority_queue->count(); |
270 return _priority_queue->count(); |
182 } |
271 } |