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