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