2
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
25
#include "precompiled.hpp"
26
#include "classfile/classLoaderData.hpp"
27
#include "classfile/javaClasses.hpp"
28
#include "gc/shared/oopStorage.inline.hpp"
29
#include "gc/shared/oopStorageSet.hpp"
30
#include "logging/log.hpp"
31
#include "memory/allocation.hpp"
32
#include "memory/resourceArea.hpp"
33
#include "memory/universe.hpp"
34
#include "oops/access.inline.hpp"
35
#include "oops/method.hpp"
36
#include "oops/oop.inline.hpp"
37
#include "oops/weakHandle.inline.hpp"
38
#include "prims/resolvedMethodTable.hpp"
39
#include "runtime/atomic.hpp"
40
#include "runtime/handles.inline.hpp"
41
#include "runtime/interfaceSupport.inline.hpp"
42
#include "runtime/mutexLocker.hpp"
43
#include "runtime/safepointVerifiers.hpp"
44
#include "runtime/timerTrace.hpp"
45
#include "utilities/concurrentHashTable.inline.hpp"
46
#include "utilities/concurrentHashTableTasks.inline.hpp"
47
#include "utilities/macros.hpp"
50
static const size_t END_SIZE = 24;
51
// If a chain gets to 32 something might be wrong
52
static const size_t GROW_HINT = 32;
54
static const size_t ResolvedMethodTableSizeLog = 10;
56
static unsigned int method_hash(const Method* method) {
57
unsigned int hash = method->method_holder()->class_loader_data()->identity_hash();
58
hash = (hash * 31) ^ method->klass_name()->identity_hash();
59
hash = (hash * 31) ^ method->name()->identity_hash();
60
hash = (hash * 31) ^ method->signature()->identity_hash();
64
typedef ConcurrentHashTable<ResolvedMethodTableConfig,
65
mtClass> ResolvedMethodTableHash;
67
class ResolvedMethodTableConfig : public AllStatic {
70
typedef WeakHandle Value;
72
static uintx get_hash(Value const& value, bool* is_dead) {
73
oop val_oop = value.peek();
74
if (val_oop == nullptr) {
79
Method* method = java_lang_invoke_ResolvedMethodName::vmtarget(val_oop);
80
return method_hash(method);
83
// We use default allocation/deallocation but counted
84
static void* allocate_node(void* context, size_t size, Value const& value) {
85
ResolvedMethodTable::item_added();
86
return AllocateHeap(size, mtClass);
88
static void free_node(void* context, void* memory, Value& value) {
89
value.release(ResolvedMethodTable::_oop_storage);
91
ResolvedMethodTable::item_removed();
95
static ResolvedMethodTableHash* _local_table = nullptr;
96
static size_t _current_size = (size_t)1 << ResolvedMethodTableSizeLog;
98
volatile bool ResolvedMethodTable::_has_work = false;
99
OopStorage* ResolvedMethodTable::_oop_storage;
101
volatile size_t _items_count = 0;
103
void ResolvedMethodTable::create_table() {
104
_local_table = new ResolvedMethodTableHash(ResolvedMethodTableSizeLog, END_SIZE, GROW_HINT);
105
log_trace(membername, table)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
106
_current_size, ResolvedMethodTableSizeLog);
107
_oop_storage = OopStorageSet::create_weak("ResolvedMethodTable Weak", mtClass);
108
_oop_storage->register_num_dead_callback(&gc_notification);
111
size_t ResolvedMethodTable::table_size() {
112
return (size_t)1 << _local_table->get_size_log2(Thread::current());
115
class ResolvedMethodTableLookup : StackObj {
119
const Method* _method;
123
ResolvedMethodTableLookup(Thread* thread, uintx hash, const Method* key)
124
: _thread(thread), _hash(hash), _method(key) {
126
uintx get_hash() const {
129
bool equals(WeakHandle* value) {
130
oop val_oop = value->peek();
131
if (val_oop == nullptr) {
134
bool equals = _method == java_lang_invoke_ResolvedMethodName::vmtarget(val_oop);
138
// Need to resolve weak handle and Handleize through possible safepoint.
139
_found = Handle(_thread, value->resolve());
142
bool is_dead(WeakHandle* value) {
143
oop val_oop = value->peek();
144
return val_oop == nullptr;
149
class ResolvedMethodGet : public StackObj {
151
const Method* _method;
154
ResolvedMethodGet(Thread* thread, const Method* method) : _thread(thread), _method(method) {}
155
void operator()(WeakHandle* val) {
156
oop result = val->resolve();
157
assert(result != nullptr, "Result should be reachable");
158
_return = Handle(_thread, result);
165
LogTarget(Trace, membername, table) log;
166
if (log.is_enabled()) {
168
log.print("ResolvedMethod entry found for %s",
169
_method->name_and_sig_as_C_string());
174
oop ResolvedMethodTable::find_method(const Method* method) {
175
Thread* thread = Thread::current();
177
ResolvedMethodTableLookup lookup(thread, method_hash(method), method);
178
ResolvedMethodGet rmg(thread, method);
179
_local_table->get(thread, lookup, rmg);
181
return rmg.get_res_oop();
184
static void log_insert(const Method* method) {
185
LogTarget(Debug, membername, table) log;
186
if (log.is_enabled()) {
188
log.print("ResolvedMethod entry added for %s",
189
method->name_and_sig_as_C_string());
193
oop ResolvedMethodTable::add_method(const Method* method, Handle rmethod_name) {
194
Thread* thread = Thread::current();
196
ResolvedMethodTableLookup lookup(thread, method_hash(method), method);
197
ResolvedMethodGet rmg(thread, method);
200
if (_local_table->get(thread, lookup, rmg)) {
201
return rmg.get_res_oop();
203
WeakHandle wh(_oop_storage, rmethod_name);
204
// The hash table takes ownership of the WeakHandle, even if it's not inserted.
205
if (_local_table->insert(thread, lookup, wh)) {
212
void ResolvedMethodTable::item_added() {
213
Atomic::inc(&_items_count);
216
void ResolvedMethodTable::item_removed() {
217
Atomic::dec(&_items_count);
218
log_trace(membername, table) ("ResolvedMethod entry removed");
221
double ResolvedMethodTable::get_load_factor() {
222
return double(_items_count)/double(_current_size);
225
double ResolvedMethodTable::get_dead_factor(size_t num_dead) {
226
return double(num_dead)/double(_current_size);
229
static const double PREF_AVG_LIST_LEN = 2.0;
230
// If we have as many dead items as 50% of the number of bucket
231
static const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5;
233
void ResolvedMethodTable::gc_notification(size_t num_dead) {
234
log_trace(membername, table)("Uncleaned items:" SIZE_FORMAT, num_dead);
240
double load_factor = get_load_factor();
241
double dead_factor = get_dead_factor(num_dead);
242
// We should clean/resize if we have more dead than alive,
243
// more items than preferred load factor or
244
// more dead items than water mark.
245
if ((dead_factor > load_factor) ||
246
(load_factor > PREF_AVG_LIST_LEN) ||
247
(dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) {
248
log_debug(membername, table)("Concurrent work triggered, live factor: %g dead factor: %g",
249
load_factor, dead_factor);
250
trigger_concurrent_work();
254
void ResolvedMethodTable::trigger_concurrent_work() {
255
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
256
Atomic::store(&_has_work, true);
257
Service_lock->notify_all();
260
bool ResolvedMethodTable::has_work() {
261
return Atomic::load_acquire(&_has_work);
264
void ResolvedMethodTable::do_concurrent_work(JavaThread* jt) {
265
double load_factor = get_load_factor();
266
log_debug(membername, table)("Concurrent work, live factor: %g", load_factor);
267
// We prefer growing, since that also removes dead items
268
if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
271
clean_dead_entries(jt);
273
Atomic::release_store(&_has_work, false);
276
void ResolvedMethodTable::grow(JavaThread* jt) {
277
ResolvedMethodTableHash::GrowTask gt(_local_table);
278
if (!gt.prepare(jt)) {
281
log_trace(membername, table)("Started to grow");
283
TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf));
284
while (gt.do_task(jt)) {
287
ThreadBlockInVM tbivm(jt);
293
_current_size = table_size();
294
log_info(membername, table)("Grown to size:" SIZE_FORMAT, _current_size);
297
struct ResolvedMethodTableDoDelete : StackObj {
298
void operator()(WeakHandle* val) {
303
struct ResolvedMethodTableDeleteCheck : StackObj {
306
ResolvedMethodTableDeleteCheck() : _count(0), _item(0) {}
307
bool operator()(WeakHandle* val) {
309
oop tmp = val->peek();
310
if (tmp == nullptr) {
319
void ResolvedMethodTable::clean_dead_entries(JavaThread* jt) {
320
ResolvedMethodTableHash::BulkDeleteTask bdt(_local_table);
321
if (!bdt.prepare(jt)) {
324
ResolvedMethodTableDeleteCheck stdc;
325
ResolvedMethodTableDoDelete stdd;
327
TraceTime timer("Clean", TRACETIME_LOG(Debug, membername, table, perf));
328
while(bdt.do_task(jt, stdc, stdd)) {
331
ThreadBlockInVM tbivm(jt);
337
log_info(membername, table)("Cleaned %ld of %ld", stdc._count, stdc._item);
341
class AdjustMethodEntries : public StackObj {
342
bool* _trace_name_printed;
344
AdjustMethodEntries(bool* trace_name_printed) : _trace_name_printed(trace_name_printed) {};
345
bool operator()(WeakHandle* entry) {
346
oop mem_name = entry->peek();
347
if (mem_name == nullptr) {
352
Method* old_method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(mem_name);
354
if (old_method->is_old()) {
356
Method* new_method = (old_method->is_deleted()) ?
357
Universe::throw_no_such_method_error() :
358
old_method->get_new_method();
359
java_lang_invoke_ResolvedMethodName::set_vmtarget(mem_name, new_method);
362
if (!(*_trace_name_printed)) {
363
log_info(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
364
*_trace_name_printed = true;
366
log_debug(redefine, class, update, constantpool)
367
("ResolvedMethod method update: %s(%s)",
368
new_method->name()->as_C_string(), new_method->signature()->as_C_string());
375
// It is called at safepoint only for RedefineClasses
376
void ResolvedMethodTable::adjust_method_entries(bool * trace_name_printed) {
377
assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
378
// For each entry in RMT, change to new method
379
AdjustMethodEntries adjust(trace_name_printed);
380
_local_table->do_safepoint_scan(adjust);
382
#endif // INCLUDE_JVMTI
385
class VerifyResolvedMethod : StackObj {
387
bool operator()(WeakHandle* val) {
388
oop obj = val->peek();
389
if (obj != nullptr) {
390
Method* method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(obj);
391
guarantee(method->is_method(), "Must be");
392
guarantee(!method->is_old(), "Must be");
398
size_t ResolvedMethodTable::items_count() {
402
void ResolvedMethodTable::verify() {
403
VerifyResolvedMethod vcs;
404
if (!_local_table->try_scan(Thread::current(), vcs)) {
405
log_info(membername, table)("verify unavailable at this moment");