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 "memory/allocation.hpp"
26 #include "memory/universe.hpp"
27 #include "oops/oop.inline.hpp"
28 #include "oops/weakHandle.inline.hpp"
29 #include "prims/jvmtiExport.hpp"
30 #include "prims/jvmtiTagMapTable.hpp"
31
32
33 JvmtiTagMapKey::JvmtiTagMapKey(oop obj) : _obj(obj) {}
34
35 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) {
36 // move object into WeakHandle when copying into the table
37 if (src._obj != nullptr) {
38
39 // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
40 // a heap walk. The object needs to be kept alive when it is published.
41 Universe::heap()->keep_alive(src._obj);
42
43 _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj);
44 } else {
45 // resizing needs to create a copy.
46 _wh = src._wh;
47 }
48 // obj is always null after a copy.
49 _obj = nullptr;
50 }
51
52 void JvmtiTagMapKey::release_weak_handle() {
53 _wh.release(JvmtiExport::weak_tag_storage());
54 }
55
56 oop JvmtiTagMapKey::object() const {
57 assert(_obj == nullptr, "Must have a handle and not object");
58 return _wh.resolve();
59 }
60
61 oop JvmtiTagMapKey::object_no_keepalive() const {
62 assert(_obj == nullptr, "Must have a handle and not object");
63 return _wh.peek();
64 }
65
66 static const int INITIAL_TABLE_SIZE = 1007;
67 static const int MAX_TABLE_SIZE = 0x3fffffff;
68
69 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
70
71 void JvmtiTagMapTable::clear() {
72 struct RemoveAll {
73 bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
74 entry.release_weak_handle();
75 return true;
76 }
77 } remove_all;
78 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
79 // while iterating over all the elements of the table. If the do_entry() method returns true the element
80 // will be removed.
81 // In this case, we always return true from do_entry to clear all the elements.
82 _table.unlink(&remove_all);
83
84 assert(_table.number_of_entries() == 0, "should have removed all entries");
85 }
86
87 JvmtiTagMapTable::~JvmtiTagMapTable() {
88 clear();
89 }
90
91 jlong JvmtiTagMapTable::find(oop obj) {
92 if (is_empty()) {
93 return 0;
94 }
95
96 if (obj->fast_no_hash_check()) {
97 // Objects in the table all have a hashcode.
98 return 0;
99 }
100
101 JvmtiTagMapKey jtme(obj);
102 jlong* found = _table.get(jtme);
103 return found == nullptr ? 0 : *found;
104 }
105
106 void JvmtiTagMapTable::add(oop obj, jlong tag) {
107 JvmtiTagMapKey new_entry(obj);
108 bool is_added;
109 if (obj->fast_no_hash_check()) {
110 // Can't be in the table so add it fast.
111 is_added = _table.put_when_absent(new_entry, tag);
112 } else {
113 jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
114 *value = tag; // assign the new tag
115 }
116 if (is_added) {
117 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
118 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
119 log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
120 _table.table_size(), _table.number_of_entries(), max_bucket_size);
121 }
122 }
123 }
124
125 void JvmtiTagMapTable::remove(oop obj) {
126 JvmtiTagMapKey jtme(obj);
127 auto clean = [] (JvmtiTagMapKey& entry, jlong tag) {
128 entry.release_weak_handle();
129 };
130 _table.remove(jtme, clean);
131 }
132
133 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
134 _table.iterate(closure);
135 }
136
137 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
138 struct IsDead {
139 GrowableArray<jlong>* _objects;
140 IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
141 bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
142 if (entry.object_no_keepalive() == nullptr) {
143 if (_objects != nullptr) {
144 _objects->append(tag);
145 }
146 entry.release_weak_handle();
147 return true;
148 }
149 return false;;
150 }
151 } is_dead(objects);
152 _table.unlink(&is_dead);
153 }
|
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 "memory/allocation.hpp"
26 #include "memory/universe.hpp"
27 #include "oops/fieldStreams.inline.hpp"
28 #include "oops/oop.inline.hpp"
29 #include "oops/weakHandle.inline.hpp"
30 #include "prims/jvmtiExport.hpp"
31 #include "prims/jvmtiTagMapTable.hpp"
32
33
34 JvmtiTagMapKey::JvmtiTagMapKey(oop obj) : _obj(obj) {}
35
36 JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) : _h() {
37 // move object into Handle when copying into the table
38 if (src._obj != nullptr) {
39 _is_weak = !src._obj->klass()->is_inline_klass();
40
41 // obj was read with AS_NO_KEEPALIVE, or equivalent, like during
42 // a heap walk. The object needs to be kept alive when it is published.
43 Universe::heap()->keep_alive(src._obj);
44
45 if (_is_weak) {
46 _wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj);
47 } else {
48 _h = OopHandle(JvmtiExport::jvmti_oop_storage(), src._obj);
49 }
50 } else {
51 // resizing needs to create a copy.
52 if (_is_weak) {
53 _wh = src._wh;
54 } else {
55 _h = src._h;
56 }
57 }
58 // obj is always null after a copy.
59 _obj = nullptr;
60 }
61
62 void JvmtiTagMapKey::release_handle() {
63 if (_is_weak) {
64 _wh.release(JvmtiExport::weak_tag_storage());
65 } else {
66 _h.release(JvmtiExport::jvmti_oop_storage());
67 }
68 }
69
70 oop JvmtiTagMapKey::object() const {
71 assert(_obj == nullptr, "Must have a handle and not object");
72 return _is_weak ? _wh.resolve() : _h.resolve();
73 }
74
75 oop JvmtiTagMapKey::object_no_keepalive() const {
76 assert(_obj == nullptr, "Must have a handle and not object");
77 return _is_weak ? _wh.peek() : _h.peek();
78 }
79
80 unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
81 oop obj = entry._obj;
82 assert(obj != nullptr, "must lookup obj to hash");
83 if (obj->is_inline_type()) {
84 // For inline types, use the klass as a hash code and let the equals match the obj.
85 // It might have a long bucket but TBD to improve this if a customer situation arises.
86 return (unsigned)((int64_t)obj->klass() >> 3);
87 } else {
88 return (unsigned)obj->identity_hash();
89 }
90 }
91
92 static bool equal_oops(oop obj1, oop obj2); // forward declaration
93
94 static bool equal_fields(char type, oop obj1, oop obj2, int offset) {
95 switch (type) {
96 case JVM_SIGNATURE_BOOLEAN:
97 return obj1->bool_field(offset) == obj2->bool_field(offset);
98 case JVM_SIGNATURE_CHAR:
99 return obj1->char_field(offset) == obj2->char_field(offset);
100 case JVM_SIGNATURE_FLOAT:
101 return obj1->float_field(offset) == obj2->float_field(offset);
102 case JVM_SIGNATURE_DOUBLE:
103 return obj1->double_field(offset) == obj2->double_field(offset);
104 case JVM_SIGNATURE_BYTE:
105 return obj1->byte_field(offset) == obj2->byte_field(offset);
106 case JVM_SIGNATURE_SHORT:
107 return obj1->short_field(offset) == obj2->short_field(offset);
108 case JVM_SIGNATURE_INT:
109 return obj1->int_field(offset) == obj2->int_field(offset);
110 case JVM_SIGNATURE_LONG:
111 return obj1->long_field(offset) == obj2->long_field(offset);
112 case JVM_SIGNATURE_CLASS:
113 case JVM_SIGNATURE_ARRAY:
114 return equal_oops(obj1->obj_field(offset), obj2->obj_field(offset));
115 }
116 ShouldNotReachHere();
117 }
118
119 // For heap-allocated objects offset is 0 and 'klass' is obj1->klass() (== obj2->klass()).
120 // For flattened objects offset is the offset in the holder object, 'klass' is inlined object class.
121 static bool equal_value_objects(oop obj1, oop obj2, InlineKlass* klass, int offset) {
122 for (JavaFieldStream fld(klass); !fld.done(); fld.next()) {
123 // ignore static fields
124 if (fld.access_flags().is_static()) {
125 continue;
126 }
127 int field_offset = offset + fld.offset() - (offset > 0 ? klass->payload_offset() : 0);
128 if (fld.is_flat()) { // flat value field
129 InstanceKlass* holder_klass = fld.field_holder();
130 InlineKlass* field_klass = holder_klass->get_inline_type_field_klass(fld.index());
131 if (!equal_value_objects(obj1, obj2, field_klass, field_offset)) {
132 return false;
133 }
134 } else {
135 if (!equal_fields(fld.signature()->char_at(0), obj1, obj2, field_offset)) {
136 return false;
137 }
138 }
139 }
140 return true;
141 }
142
143 static bool equal_oops(oop obj1, oop obj2) {
144 if (obj1 == obj2) {
145 return true;
146 }
147
148 if (EnableValhalla) {
149 if (obj1 != nullptr && obj2 != nullptr && obj1->klass() == obj2->klass() && obj1->is_inline_type()) {
150 InlineKlass* vk = InlineKlass::cast(obj1->klass());
151 return equal_value_objects(obj1, obj2, vk, 0);
152 }
153 }
154 return false;
155 }
156
157 bool JvmtiTagMapKey::equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs) {
158 oop lhs_obj = lhs._obj != nullptr ? lhs._obj : lhs.object_no_keepalive();
159 oop rhs_obj = rhs._obj != nullptr ? rhs._obj : rhs.object_no_keepalive();
160 return equal_oops(lhs_obj, rhs_obj);
161 }
162
163 // Inline types don't use hash for this table.
164 static inline bool fast_no_hash_check(oop obj) {
165 return (obj->fast_no_hash_check() && !obj->is_inline_type());
166 }
167
168 static const int INITIAL_TABLE_SIZE = 1007;
169 static const int MAX_TABLE_SIZE = 0x3fffffff;
170
171 JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
172
173 void JvmtiTagMapTable::clear() {
174 struct RemoveAll {
175 bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
176 entry.release_handle();
177 return true;
178 }
179 } remove_all;
180 // The unlink method of ResourceHashTable gets a pointer to a type whose 'do_entry(K,V)' method is callled
181 // while iterating over all the elements of the table. If the do_entry() method returns true the element
182 // will be removed.
183 // In this case, we always return true from do_entry to clear all the elements.
184 _table.unlink(&remove_all);
185
186 assert(_table.number_of_entries() == 0, "should have removed all entries");
187 }
188
189 JvmtiTagMapTable::~JvmtiTagMapTable() {
190 clear();
191 }
192
193 jlong JvmtiTagMapTable::find(oop obj) {
194 if (is_empty()) {
195 return 0;
196 }
197
198 if (fast_no_hash_check(obj)) {
199 // Objects in the table all have a hashcode, unless inlined types.
200 return 0;
201 }
202
203 JvmtiTagMapKey jtme(obj);
204 jlong* found = _table.get(jtme);
205 return found == nullptr ? 0 : *found;
206 }
207
208 void JvmtiTagMapTable::add(oop obj, jlong tag) {
209 JvmtiTagMapKey new_entry(obj);
210 bool is_added;
211 if (fast_no_hash_check(obj)) {
212 // Can't be in the table so add it fast.
213 is_added = _table.put_when_absent(new_entry, tag);
214 } else {
215 jlong* value = _table.put_if_absent(new_entry, tag, &is_added);
216 *value = tag; // assign the new tag
217 }
218 if (is_added) {
219 if (_table.maybe_grow(5, true /* use_large_table_sizes */)) {
220 int max_bucket_size = DEBUG_ONLY(_table.verify()) NOT_DEBUG(0);
221 log_info(jvmti, table) ("JvmtiTagMap table resized to %d for %d entries max bucket %d",
222 _table.table_size(), _table.number_of_entries(), max_bucket_size);
223 }
224 }
225 }
226
227 void JvmtiTagMapTable::remove(oop obj) {
228 JvmtiTagMapKey jtme(obj);
229 auto clean = [] (JvmtiTagMapKey& entry, jlong tag) {
230 entry.release_handle();
231 };
232 _table.remove(jtme, clean);
233 }
234
235 void JvmtiTagMapTable::entry_iterate(JvmtiTagMapKeyClosure* closure) {
236 _table.iterate(closure);
237 }
238
239 void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
240 struct IsDead {
241 GrowableArray<jlong>* _objects;
242 IsDead(GrowableArray<jlong>* objects) : _objects(objects) {}
243 bool do_entry(JvmtiTagMapKey& entry, jlong tag) {
244 if (entry.object_no_keepalive() == nullptr) {
245 if (_objects != nullptr) {
246 _objects->append(tag);
247 }
248 entry.release_handle();
249 return true;
250 }
251 return false;;
252 }
253 } is_dead(objects);
254 _table.unlink(&is_dead);
255 }
|