1 | /* |
2 | * Copyright (c) 2015, 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 | */ |
24 | |
25 | #include "precompiled.hpp" |
26 | #include "code/nmethod.hpp" |
27 | #include "code/dependencies.hpp" |
28 | #include "code/dependencyContext.hpp" |
29 | #include "memory/resourceArea.hpp" |
30 | #include "runtime/atomic.hpp" |
31 | #include "runtime/perfData.hpp" |
32 | #include "utilities/exceptions.hpp" |
33 | |
34 | PerfCounter* DependencyContext::_perf_total_buckets_allocated_count = NULL; |
35 | PerfCounter* DependencyContext::_perf_total_buckets_deallocated_count = NULL; |
36 | PerfCounter* DependencyContext::_perf_total_buckets_stale_count = NULL; |
37 | PerfCounter* DependencyContext::_perf_total_buckets_stale_acc_count = NULL; |
38 | nmethodBucket* volatile DependencyContext::_purge_list = NULL; |
39 | volatile uint64_t DependencyContext::_cleaning_epoch = 0; |
40 | uint64_t DependencyContext::_cleaning_epoch_monotonic = 0; |
41 | |
42 | void dependencyContext_init() { |
43 | DependencyContext::init(); |
44 | } |
45 | |
46 | void DependencyContext::init() { |
47 | if (UsePerfData) { |
48 | EXCEPTION_MARK; |
49 | _perf_total_buckets_allocated_count = |
50 | PerfDataManager::create_counter(SUN_CI, "nmethodBucketsAllocated" , PerfData::U_Events, CHECK); |
51 | _perf_total_buckets_deallocated_count = |
52 | PerfDataManager::create_counter(SUN_CI, "nmethodBucketsDeallocated" , PerfData::U_Events, CHECK); |
53 | _perf_total_buckets_stale_count = |
54 | PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStale" , PerfData::U_Events, CHECK); |
55 | _perf_total_buckets_stale_acc_count = |
56 | PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStaleAccumulated" , PerfData::U_Events, CHECK); |
57 | } |
58 | } |
59 | |
60 | // |
61 | // Walk the list of dependent nmethods searching for nmethods which |
62 | // are dependent on the changes that were passed in and mark them for |
63 | // deoptimization. Returns the number of nmethods found. |
64 | // |
65 | int DependencyContext::mark_dependent_nmethods(DepChange& changes) { |
66 | int found = 0; |
67 | for (nmethodBucket* b = dependencies_not_unloading(); b != NULL; b = b->next_not_unloading()) { |
68 | nmethod* nm = b->get_nmethod(); |
69 | // since dependencies aren't removed until an nmethod becomes a zombie, |
70 | // the dependency list may contain nmethods which aren't alive. |
71 | if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { |
72 | if (TraceDependencies) { |
73 | ResourceMark rm; |
74 | tty->print_cr("Marked for deoptimization" ); |
75 | changes.print(); |
76 | nm->print(); |
77 | nm->print_dependencies(); |
78 | } |
79 | changes.mark_for_deoptimization(nm); |
80 | found++; |
81 | } |
82 | } |
83 | return found; |
84 | } |
85 | |
86 | // |
87 | // Add an nmethod to the dependency context. |
88 | // It's possible that an nmethod has multiple dependencies on a klass |
89 | // so a count is kept for each bucket to guarantee that creation and |
90 | // deletion of dependencies is consistent. |
91 | // |
92 | void DependencyContext::add_dependent_nmethod(nmethod* nm) { |
93 | assert_lock_strong(CodeCache_lock); |
94 | for (nmethodBucket* b = dependencies_not_unloading(); b != NULL; b = b->next_not_unloading()) { |
95 | if (nm == b->get_nmethod()) { |
96 | b->increment(); |
97 | return; |
98 | } |
99 | } |
100 | nmethodBucket* new_head = new nmethodBucket(nm, NULL); |
101 | for (;;) { |
102 | nmethodBucket* head = Atomic::load(_dependency_context_addr); |
103 | new_head->set_next(head); |
104 | if (Atomic::cmpxchg(new_head, _dependency_context_addr, head) == head) { |
105 | break; |
106 | } |
107 | } |
108 | if (UsePerfData) { |
109 | _perf_total_buckets_allocated_count->inc(); |
110 | } |
111 | } |
112 | |
113 | void DependencyContext::release(nmethodBucket* b) { |
114 | bool expunge = Atomic::load(&_cleaning_epoch) == 0; |
115 | if (expunge) { |
116 | assert_locked_or_safepoint(CodeCache_lock); |
117 | delete b; |
118 | if (UsePerfData) { |
119 | _perf_total_buckets_deallocated_count->inc(); |
120 | } |
121 | } else { |
122 | // Mark the context as having stale entries, since it is not safe to |
123 | // expunge the list right now. |
124 | for (;;) { |
125 | nmethodBucket* purge_list_head = Atomic::load(&_purge_list); |
126 | b->set_purge_list_next(purge_list_head); |
127 | if (Atomic::cmpxchg(b, &_purge_list, purge_list_head) == purge_list_head) { |
128 | break; |
129 | } |
130 | } |
131 | if (UsePerfData) { |
132 | _perf_total_buckets_stale_count->inc(); |
133 | _perf_total_buckets_stale_acc_count->inc(); |
134 | } |
135 | } |
136 | } |
137 | |
138 | // |
139 | // Remove an nmethod dependency from the context. |
140 | // Decrement count of the nmethod in the dependency list and, optionally, remove |
141 | // the bucket completely when the count goes to 0. This method must find |
142 | // a corresponding bucket otherwise there's a bug in the recording of dependencies. |
143 | // Can be called concurrently by parallel GC threads. |
144 | // |
145 | void DependencyContext::remove_dependent_nmethod(nmethod* nm) { |
146 | assert_locked_or_safepoint(CodeCache_lock); |
147 | nmethodBucket* first = dependencies_not_unloading(); |
148 | nmethodBucket* last = NULL; |
149 | for (nmethodBucket* b = first; b != NULL; b = b->next_not_unloading()) { |
150 | if (nm == b->get_nmethod()) { |
151 | int val = b->decrement(); |
152 | guarantee(val >= 0, "Underflow: %d" , val); |
153 | if (val == 0) { |
154 | if (last == NULL) { |
155 | // If there was not a head that was not unloading, we can set a new |
156 | // head without a CAS, because we know there is no contending cleanup. |
157 | set_dependencies(b->next_not_unloading()); |
158 | } else { |
159 | // Only supports a single inserting thread (protected by CodeCache_lock) |
160 | // for now. Therefore, the next pointer only competes with another cleanup |
161 | // operation. That interaction does not need a CAS. |
162 | last->set_next(b->next_not_unloading()); |
163 | } |
164 | release(b); |
165 | } |
166 | return; |
167 | } |
168 | last = b; |
169 | } |
170 | } |
171 | |
172 | // |
173 | // Reclaim all unused buckets. |
174 | // |
175 | void DependencyContext::purge_dependency_contexts() { |
176 | int removed = 0; |
177 | for (nmethodBucket* b = _purge_list; b != NULL;) { |
178 | nmethodBucket* next = b->purge_list_next(); |
179 | removed++; |
180 | delete b; |
181 | b = next; |
182 | } |
183 | if (UsePerfData && removed > 0) { |
184 | _perf_total_buckets_deallocated_count->inc(removed); |
185 | } |
186 | _purge_list = NULL; |
187 | } |
188 | |
189 | // |
190 | // Cleanup a dependency context by unlinking and placing all dependents corresponding |
191 | // to is_unloading nmethods on a purge list, which will be deleted later when it is safe. |
192 | void DependencyContext::clean_unloading_dependents() { |
193 | if (!claim_cleanup()) { |
194 | // Somebody else is cleaning up this dependency context. |
195 | return; |
196 | } |
197 | // Walk the nmethodBuckets and move dead entries on the purge list, which will |
198 | // be deleted during ClassLoaderDataGraph::purge(). |
199 | nmethodBucket* b = dependencies_not_unloading(); |
200 | while (b != NULL) { |
201 | nmethodBucket* next = b->next_not_unloading(); |
202 | b = next; |
203 | } |
204 | } |
205 | |
206 | // |
207 | // Invalidate all dependencies in the context |
208 | int DependencyContext::remove_all_dependents() { |
209 | nmethodBucket* b = dependencies_not_unloading(); |
210 | set_dependencies(NULL); |
211 | int marked = 0; |
212 | int removed = 0; |
213 | while (b != NULL) { |
214 | nmethod* nm = b->get_nmethod(); |
215 | if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) { |
216 | nm->mark_for_deoptimization(); |
217 | marked++; |
218 | } |
219 | nmethodBucket* next = b->next_not_unloading(); |
220 | removed++; |
221 | release(b); |
222 | b = next; |
223 | } |
224 | if (UsePerfData && removed > 0) { |
225 | _perf_total_buckets_deallocated_count->inc(removed); |
226 | } |
227 | return marked; |
228 | } |
229 | |
230 | #ifndef PRODUCT |
231 | void DependencyContext::print_dependent_nmethods(bool verbose) { |
232 | int idx = 0; |
233 | for (nmethodBucket* b = dependencies_not_unloading(); b != NULL; b = b->next_not_unloading()) { |
234 | nmethod* nm = b->get_nmethod(); |
235 | tty->print("[%d] count=%d { " , idx++, b->count()); |
236 | if (!verbose) { |
237 | nm->print_on(tty, "nmethod" ); |
238 | tty->print_cr(" } " ); |
239 | } else { |
240 | nm->print(); |
241 | nm->print_dependencies(); |
242 | tty->print_cr("--- } " ); |
243 | } |
244 | } |
245 | } |
246 | |
247 | bool DependencyContext::is_dependent_nmethod(nmethod* nm) { |
248 | for (nmethodBucket* b = dependencies_not_unloading(); b != NULL; b = b->next_not_unloading()) { |
249 | if (nm == b->get_nmethod()) { |
250 | #ifdef ASSERT |
251 | int count = b->count(); |
252 | assert(count >= 0, "count shouldn't be negative: %d" , count); |
253 | #endif |
254 | return true; |
255 | } |
256 | } |
257 | return false; |
258 | } |
259 | |
260 | #endif //PRODUCT |
261 | |
262 | int nmethodBucket::decrement() { |
263 | return Atomic::sub(1, &_count); |
264 | } |
265 | |
266 | // We use a monotonically increasing epoch counter to track the last epoch a given |
267 | // dependency context was cleaned. GC threads claim cleanup tasks by performing |
268 | // a CAS on this value. |
269 | bool DependencyContext::claim_cleanup() { |
270 | uint64_t cleaning_epoch = Atomic::load(&_cleaning_epoch); |
271 | uint64_t last_cleanup = Atomic::load(_last_cleanup_addr); |
272 | if (last_cleanup >= cleaning_epoch) { |
273 | return false; |
274 | } |
275 | return Atomic::cmpxchg(cleaning_epoch, _last_cleanup_addr, last_cleanup) == last_cleanup; |
276 | } |
277 | |
278 | // Retrieve the first nmethodBucket that has a dependent that does not correspond to |
279 | // an is_unloading nmethod. Any nmethodBucket entries observed from the original head |
280 | // that is_unloading() will be unlinked and placed on the purge list. |
281 | nmethodBucket* DependencyContext::dependencies_not_unloading() { |
282 | for (;;) { |
283 | // Need acquire becase the read value could come from a concurrent insert. |
284 | nmethodBucket* head = OrderAccess::load_acquire(_dependency_context_addr); |
285 | if (head == NULL || !head->get_nmethod()->is_unloading()) { |
286 | return head; |
287 | } |
288 | nmethodBucket* head_next = head->next(); |
289 | OrderAccess::loadload(); |
290 | if (Atomic::load(_dependency_context_addr) != head) { |
291 | // Unstable load of head w.r.t. head->next |
292 | continue; |
293 | } |
294 | if (Atomic::cmpxchg(head_next, _dependency_context_addr, head) == head) { |
295 | // Release is_unloading entries if unlinking was claimed |
296 | DependencyContext::release(head); |
297 | } |
298 | } |
299 | } |
300 | |
301 | // Relaxed accessors |
302 | void DependencyContext::set_dependencies(nmethodBucket* b) { |
303 | Atomic::store(b, _dependency_context_addr); |
304 | } |
305 | |
306 | nmethodBucket* DependencyContext::dependencies() { |
307 | return Atomic::load(_dependency_context_addr); |
308 | } |
309 | |
310 | // After the gc_prologue, the dependency contexts may be claimed by the GC |
311 | // and releasing of nmethodBucket entries will be deferred and placed on |
312 | // a purge list to be deleted later. |
313 | void DependencyContext::cleaning_start() { |
314 | assert(SafepointSynchronize::is_at_safepoint(), "must be" ); |
315 | uint64_t epoch = ++_cleaning_epoch_monotonic; |
316 | Atomic::store(epoch, &_cleaning_epoch); |
317 | } |
318 | |
319 | // The epilogue marks the end of dependency context cleanup by the GC, |
320 | // and also makes subsequent releases of nmethodBuckets cause immediate |
321 | // deletion. It is okay to delay calling of cleaning_end() to a concurrent |
322 | // phase, subsequent to the safepoint operation in which cleaning_start() |
323 | // was called. That allows dependency contexts to be cleaned concurrently. |
324 | void DependencyContext::cleaning_end() { |
325 | uint64_t epoch = 0; |
326 | Atomic::store(epoch, &_cleaning_epoch); |
327 | } |
328 | |
329 | // This function skips over nmethodBuckets in the list corresponding to |
330 | // nmethods that are is_unloading. This allows exposing a view of the |
331 | // dependents as-if they were already cleaned, despite being cleaned |
332 | // concurrently. Any entry observed that is_unloading() will be unlinked |
333 | // and placed on the purge list. |
334 | nmethodBucket* nmethodBucket::next_not_unloading() { |
335 | for (;;) { |
336 | // Do not need acquire because the loaded entry can never be |
337 | // concurrently inserted. |
338 | nmethodBucket* next = Atomic::load(&_next); |
339 | if (next == NULL || !next->get_nmethod()->is_unloading()) { |
340 | return next; |
341 | } |
342 | nmethodBucket* next_next = Atomic::load(&next->_next); |
343 | OrderAccess::loadload(); |
344 | if (Atomic::load(&_next) != next) { |
345 | // Unstable load of next w.r.t. next->next |
346 | continue; |
347 | } |
348 | if (Atomic::cmpxchg(next_next, &_next, next) == next) { |
349 | // Release is_unloading entries if unlinking was claimed |
350 | DependencyContext::release(next); |
351 | } |
352 | } |
353 | } |
354 | |
355 | // Relaxed accessors |
356 | nmethodBucket* nmethodBucket::next() { |
357 | return Atomic::load(&_next); |
358 | } |
359 | |
360 | void nmethodBucket::set_next(nmethodBucket* b) { |
361 | Atomic::store(b, &_next); |
362 | } |
363 | |
364 | nmethodBucket* nmethodBucket::purge_list_next() { |
365 | return Atomic::load(&_purge_list_next); |
366 | } |
367 | |
368 | void nmethodBucket::set_purge_list_next(nmethodBucket* b) { |
369 | Atomic::store(b, &_purge_list_next); |
370 | } |
371 | |