1 /*
2 * Copyright (c) 2017, 2019, 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 "classfile/javaClasses.hpp"
27 #include "gc/shared/oopStorage.inline.hpp"
28 #include "gc/shared/oopStorageSet.hpp"
29 #include "logging/log.hpp"
30 #include "memory/allocation.hpp"
31 #include "memory/resourceArea.hpp"
32 #include "memory/universe.hpp"
33 #include "oops/access.inline.hpp"
34 #include "oops/method.hpp"
35 #include "oops/oop.inline.hpp"
36 #include "oops/weakHandle.inline.hpp"
37 #include "prims/resolvedMethodTable.hpp"
38 #include "runtime/atomic.hpp"
39 #include "runtime/handles.inline.hpp"
40 #include "runtime/interfaceSupport.inline.hpp"
41 #include "runtime/mutexLocker.hpp"
42 #include "runtime/safepointVerifiers.hpp"
43 #include "runtime/timerTrace.hpp"
44 #include "utilities/concurrentHashTable.inline.hpp"
45 #include "utilities/concurrentHashTableTasks.inline.hpp"
46 #include "utilities/macros.hpp"
47
48 // 2^24 is max size
49 static const size_t END_SIZE = 24;
50 // If a chain gets to 32 something might be wrong
51 static const size_t GROW_HINT = 32;
52
53 static const size_t ResolvedMethodTableSizeLog = 10;
54
method_hash(const Method * method)55 unsigned int method_hash(const Method* method) {
56 unsigned int name_hash = method->name()->identity_hash();
57 unsigned int signature_hash = method->signature()->identity_hash();
58 return name_hash ^ signature_hash;
59 }
60
61 typedef ConcurrentHashTable<ResolvedMethodTableConfig,
62 mtClass> ResolvedMethodTableHash;
63
64 class ResolvedMethodTableConfig : public AllStatic {
65 private:
66 public:
67 typedef WeakHandle<vm_resolved_method_table_data> Value;
68
get_hash(Value const & value,bool * is_dead)69 static uintx get_hash(Value const& value, bool* is_dead) {
70 oop val_oop = value.peek();
71 if (val_oop == NULL) {
72 *is_dead = true;
73 return 0;
74 }
75 *is_dead = false;
76 Method* method = java_lang_invoke_ResolvedMethodName::vmtarget(val_oop);
77 return method_hash(method);
78 }
79
80 // We use default allocation/deallocation but counted
allocate_node(size_t size,Value const & value)81 static void* allocate_node(size_t size, Value const& value) {
82 ResolvedMethodTable::item_added();
83 return AllocateHeap(size, mtClass);
84 }
free_node(void * memory,Value const & value)85 static void free_node(void* memory, Value const& value) {
86 value.release();
87 FreeHeap(memory);
88 ResolvedMethodTable::item_removed();
89 }
90 };
91
92 static ResolvedMethodTableHash* _local_table = NULL;
93 static size_t _current_size = (size_t)1 << ResolvedMethodTableSizeLog;
94
95 volatile bool ResolvedMethodTable::_has_work = false;
96
97 volatile size_t _items_count = 0;
98 volatile size_t _uncleaned_items_count = 0;
99
create_table()100 void ResolvedMethodTable::create_table() {
101 _local_table = new ResolvedMethodTableHash(ResolvedMethodTableSizeLog, END_SIZE, GROW_HINT);
102 log_trace(membername, table)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
103 _current_size, ResolvedMethodTableSizeLog);
104 }
105
table_size()106 size_t ResolvedMethodTable::table_size() {
107 return (size_t)1 << _local_table->get_size_log2(Thread::current());
108 }
109
110 class ResolvedMethodTableLookup : StackObj {
111 private:
112 Thread* _thread;
113 uintx _hash;
114 const Method* _method;
115 Handle _found;
116
117 public:
ResolvedMethodTableLookup(Thread * thread,uintx hash,const Method * key)118 ResolvedMethodTableLookup(Thread* thread, uintx hash, const Method* key)
119 : _thread(thread), _hash(hash), _method(key) {
120 }
get_hash() const121 uintx get_hash() const {
122 return _hash;
123 }
equals(WeakHandle<vm_resolved_method_table_data> * value,bool * is_dead)124 bool equals(WeakHandle<vm_resolved_method_table_data>* value, bool* is_dead) {
125 oop val_oop = value->peek();
126 if (val_oop == NULL) {
127 // dead oop, mark this hash dead for cleaning
128 *is_dead = true;
129 return false;
130 }
131 bool equals = _method == java_lang_invoke_ResolvedMethodName::vmtarget(val_oop);
132 if (!equals) {
133 return false;
134 }
135 // Need to resolve weak handle and Handleize through possible safepoint.
136 _found = Handle(_thread, value->resolve());
137 return true;
138 }
139 };
140
141
142 class ResolvedMethodGet : public StackObj {
143 Thread* _thread;
144 const Method* _method;
145 Handle _return;
146 public:
ResolvedMethodGet(Thread * thread,const Method * method)147 ResolvedMethodGet(Thread* thread, const Method* method) : _thread(thread), _method(method) {}
operator ()(WeakHandle<vm_resolved_method_table_data> * val)148 void operator()(WeakHandle<vm_resolved_method_table_data>* val) {
149 oop result = val->resolve();
150 assert(result != NULL, "Result should be reachable");
151 _return = Handle(_thread, result);
152 log_get();
153 }
get_res_oop()154 oop get_res_oop() {
155 return _return();
156 }
log_get()157 void log_get() {
158 LogTarget(Trace, membername, table) log;
159 if (log.is_enabled()) {
160 ResourceMark rm;
161 log.print("ResolvedMethod entry found for %s",
162 _method->name_and_sig_as_C_string());
163 }
164 }
165 };
166
find_method(const Method * method)167 oop ResolvedMethodTable::find_method(const Method* method) {
168 Thread* thread = Thread::current();
169
170 ResolvedMethodTableLookup lookup(thread, method_hash(method), method);
171 ResolvedMethodGet rmg(thread, method);
172 _local_table->get(thread, lookup, rmg);
173
174 return rmg.get_res_oop();
175 }
176
log_insert(const Method * method)177 static void log_insert(const Method* method) {
178 LogTarget(Debug, membername, table) log;
179 if (log.is_enabled()) {
180 ResourceMark rm;
181 log.print("ResolvedMethod entry added for %s",
182 method->name_and_sig_as_C_string());
183 }
184 }
185
add_method(const Method * method,Handle rmethod_name)186 oop ResolvedMethodTable::add_method(const Method* method, Handle rmethod_name) {
187 Thread* thread = Thread::current();
188
189 ResolvedMethodTableLookup lookup(thread, method_hash(method), method);
190 ResolvedMethodGet rmg(thread, method);
191
192 while (true) {
193 if (_local_table->get(thread, lookup, rmg)) {
194 return rmg.get_res_oop();
195 }
196 WeakHandle<vm_resolved_method_table_data> wh = WeakHandle<vm_resolved_method_table_data>::create(rmethod_name);
197 // The hash table takes ownership of the WeakHandle, even if it's not inserted.
198 if (_local_table->insert(thread, lookup, wh)) {
199 log_insert(method);
200 return wh.resolve();
201 }
202 }
203 }
204
item_added()205 void ResolvedMethodTable::item_added() {
206 Atomic::inc(&_items_count);
207 }
208
item_removed()209 void ResolvedMethodTable::item_removed() {
210 Atomic::dec(&_items_count);
211 log_trace(membername, table) ("ResolvedMethod entry removed");
212 }
213
get_load_factor()214 double ResolvedMethodTable::get_load_factor() {
215 return (double)_items_count/_current_size;
216 }
217
get_dead_factor()218 double ResolvedMethodTable::get_dead_factor() {
219 return (double)_uncleaned_items_count/_current_size;
220 }
221
222 static const double PREF_AVG_LIST_LEN = 2.0;
223 // If we have as many dead items as 50% of the number of bucket
224 static const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5;
225
check_concurrent_work()226 void ResolvedMethodTable::check_concurrent_work() {
227 if (_has_work) {
228 return;
229 }
230
231 double load_factor = get_load_factor();
232 double dead_factor = get_dead_factor();
233 // We should clean/resize if we have more dead than alive,
234 // more items than preferred load factor or
235 // more dead items than water mark.
236 if ((dead_factor > load_factor) ||
237 (load_factor > PREF_AVG_LIST_LEN) ||
238 (dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) {
239 log_debug(membername, table)("Concurrent work triggered, live factor: %g dead factor: %g",
240 load_factor, dead_factor);
241 trigger_concurrent_work();
242 }
243 }
244
trigger_concurrent_work()245 void ResolvedMethodTable::trigger_concurrent_work() {
246 MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
247 _has_work = true;
248 Service_lock->notify_all();
249 }
250
do_concurrent_work(JavaThread * jt)251 void ResolvedMethodTable::do_concurrent_work(JavaThread* jt) {
252 _has_work = false;
253 double load_factor = get_load_factor();
254 log_debug(membername, table)("Concurrent work, live factor: %g", load_factor);
255 // We prefer growing, since that also removes dead items
256 if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
257 grow(jt);
258 } else {
259 clean_dead_entries(jt);
260 }
261 }
262
grow(JavaThread * jt)263 void ResolvedMethodTable::grow(JavaThread* jt) {
264 ResolvedMethodTableHash::GrowTask gt(_local_table);
265 if (!gt.prepare(jt)) {
266 return;
267 }
268 log_trace(membername, table)("Started to grow");
269 {
270 TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf));
271 while (gt.do_task(jt)) {
272 gt.pause(jt);
273 {
274 ThreadBlockInVM tbivm(jt);
275 }
276 gt.cont(jt);
277 }
278 }
279 gt.done(jt);
280 _current_size = table_size();
281 log_info(membername, table)("Grown to size:" SIZE_FORMAT, _current_size);
282 }
283
284 struct ResolvedMethodTableDoDelete : StackObj {
operator ()ResolvedMethodTableDoDelete285 void operator()(WeakHandle<vm_resolved_method_table_data>* val) {
286 /* do nothing */
287 }
288 };
289
290 struct ResolvedMethodTableDeleteCheck : StackObj {
291 long _count;
292 long _item;
ResolvedMethodTableDeleteCheckResolvedMethodTableDeleteCheck293 ResolvedMethodTableDeleteCheck() : _count(0), _item(0) {}
operator ()ResolvedMethodTableDeleteCheck294 bool operator()(WeakHandle<vm_resolved_method_table_data>* val) {
295 ++_item;
296 oop tmp = val->peek();
297 if (tmp == NULL) {
298 ++_count;
299 return true;
300 } else {
301 return false;
302 }
303 }
304 };
305
clean_dead_entries(JavaThread * jt)306 void ResolvedMethodTable::clean_dead_entries(JavaThread* jt) {
307 ResolvedMethodTableHash::BulkDeleteTask bdt(_local_table);
308 if (!bdt.prepare(jt)) {
309 return;
310 }
311 ResolvedMethodTableDeleteCheck stdc;
312 ResolvedMethodTableDoDelete stdd;
313 {
314 TraceTime timer("Clean", TRACETIME_LOG(Debug, membername, table, perf));
315 while(bdt.do_task(jt, stdc, stdd)) {
316 bdt.pause(jt);
317 {
318 ThreadBlockInVM tbivm(jt);
319 }
320 bdt.cont(jt);
321 }
322 bdt.done(jt);
323 }
324 log_info(membername, table)("Cleaned %ld of %ld", stdc._count, stdc._item);
325 }
reset_dead_counter()326 void ResolvedMethodTable::reset_dead_counter() {
327 _uncleaned_items_count = 0;
328 }
329
inc_dead_counter(size_t ndead)330 void ResolvedMethodTable::inc_dead_counter(size_t ndead) {
331 size_t total = Atomic::add(&_uncleaned_items_count, ndead);
332 log_trace(membername, table)(
333 "Uncleaned items:" SIZE_FORMAT " added: " SIZE_FORMAT " total:" SIZE_FORMAT,
334 _uncleaned_items_count, ndead, total);
335 }
336
337 // After the parallel walk this method must be called to trigger
338 // cleaning. Note it might trigger a resize instead.
finish_dead_counter()339 void ResolvedMethodTable::finish_dead_counter() {
340 check_concurrent_work();
341 }
342
343 #if INCLUDE_JVMTI
344 class AdjustMethodEntries : public StackObj {
345 bool* _trace_name_printed;
346 public:
AdjustMethodEntries(bool * trace_name_printed)347 AdjustMethodEntries(bool* trace_name_printed) : _trace_name_printed(trace_name_printed) {};
operator ()(WeakHandle<vm_resolved_method_table_data> * entry)348 bool operator()(WeakHandle<vm_resolved_method_table_data>* entry) {
349 oop mem_name = entry->peek();
350 if (mem_name == NULL) {
351 // Removed
352 return true;
353 }
354
355 Method* old_method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(mem_name);
356
357 if (old_method->is_old()) {
358
359 Method* new_method = (old_method->is_deleted()) ?
360 Universe::throw_no_such_method_error() :
361 old_method->get_new_method();
362 java_lang_invoke_ResolvedMethodName::set_vmtarget(mem_name, new_method);
363
364 ResourceMark rm;
365 if (!(*_trace_name_printed)) {
366 log_info(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
367 *_trace_name_printed = true;
368 }
369 log_debug(redefine, class, update, constantpool)
370 ("ResolvedMethod method update: %s(%s)",
371 new_method->name()->as_C_string(), new_method->signature()->as_C_string());
372 }
373
374 return true;
375 }
376 };
377
378 // It is called at safepoint only for RedefineClasses
adjust_method_entries(bool * trace_name_printed)379 void ResolvedMethodTable::adjust_method_entries(bool * trace_name_printed) {
380 assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
381 // For each entry in RMT, change to new method
382 AdjustMethodEntries adjust(trace_name_printed);
383 _local_table->do_safepoint_scan(adjust);
384 }
385 #endif // INCLUDE_JVMTI
386
387 // Verification
388 class VerifyResolvedMethod : StackObj {
389 public:
operator ()(WeakHandle<vm_resolved_method_table_data> * val)390 bool operator()(WeakHandle<vm_resolved_method_table_data>* val) {
391 oop obj = val->peek();
392 if (obj != NULL) {
393 Method* method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(obj);
394 guarantee(method->is_method(), "Must be");
395 guarantee(!method->is_old(), "Must be");
396 }
397 return true;
398 };
399 };
400
items_count()401 size_t ResolvedMethodTable::items_count() {
402 return _items_count;
403 }
404
verify()405 void ResolvedMethodTable::verify() {
406 VerifyResolvedMethod vcs;
407 if (!_local_table->try_scan(Thread::current(), vcs)) {
408 log_info(membername, table)("verify unavailable at this moment");
409 }
410 }
411