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