1 /*
2 * Copyright (c) 2020, 2025, 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 "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 }