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