Skip to content

Commit 99de2e7

Browse files
author
Alex Menkov
committed
8355560: SetTag/GetTag should consider equal value objects as the same object
1 parent 537cdc4 commit 99de2e7

File tree

4 files changed

+314
-18
lines changed

4 files changed

+314
-18
lines changed

src/hotspot/share/prims/jvmtiTagMapTable.cpp

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "memory/allocation.hpp"
2626
#include "memory/universe.hpp"
27+
#include "oops/fieldStreams.inline.hpp"
2728
#include "oops/oop.inline.hpp"
2829
#include "oops/weakHandle.inline.hpp"
2930
#include "prims/jvmtiExport.hpp"
@@ -32,35 +33,48 @@
3233

3334
JvmtiTagMapKey::JvmtiTagMapKey(oop obj) : _obj(obj) {}
3435

35-
JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) {
36-
// move object into WeakHandle when copying into the table
36+
JvmtiTagMapKey::JvmtiTagMapKey(const JvmtiTagMapKey& src) : _h() {
37+
// move object into Handle when copying into the table
3738
if (src._obj != nullptr) {
39+
_is_weak = !src._obj->klass()->is_inline_klass();
3840

3941
// obj was read with AS_NO_KEEPALIVE, or equivalent, like during
4042
// a heap walk. The object needs to be kept alive when it is published.
4143
Universe::heap()->keep_alive(src._obj);
4244

43-
_wh = WeakHandle(JvmtiExport::weak_tag_storage(), src._obj);
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+
}
4450
} else {
4551
// resizing needs to create a copy.
46-
_wh = src._wh;
52+
if (_is_weak) {
53+
_wh = src._wh;
54+
} else {
55+
_h = src._h;
56+
}
4757
}
4858
// obj is always null after a copy.
4959
_obj = nullptr;
5060
}
5161

52-
void JvmtiTagMapKey::release_weak_handle() {
53-
_wh.release(JvmtiExport::weak_tag_storage());
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+
}
5468
}
5569

5670
oop JvmtiTagMapKey::object() const {
5771
assert(_obj == nullptr, "Must have a handle and not object");
58-
return _wh.resolve();
72+
return _is_weak ? _wh.resolve() : _h.resolve();
5973
}
6074

6175
oop JvmtiTagMapKey::object_no_keepalive() const {
6276
assert(_obj == nullptr, "Must have a handle and not object");
63-
return _wh.peek();
77+
return _is_weak ? _wh.peek() : _h.peek();
6478
}
6579

6680
unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
@@ -75,6 +89,77 @@ unsigned JvmtiTagMapKey::get_hash(const JvmtiTagMapKey& entry) {
7589
}
7690
}
7791

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+
78163
// Inline types don't use hash for this table.
79164
static inline bool fast_no_hash_check(oop obj) {
80165
return (obj->fast_no_hash_check() && !obj->is_inline_type());
@@ -88,7 +173,7 @@ JvmtiTagMapTable::JvmtiTagMapTable() : _table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE
88173
void JvmtiTagMapTable::clear() {
89174
struct RemoveAll {
90175
bool do_entry(JvmtiTagMapKey& entry, const jlong& tag) {
91-
entry.release_weak_handle();
176+
entry.release_handle();
92177
return true;
93178
}
94179
} remove_all;
@@ -142,7 +227,7 @@ void JvmtiTagMapTable::add(oop obj, jlong tag) {
142227
void JvmtiTagMapTable::remove(oop obj) {
143228
JvmtiTagMapKey jtme(obj);
144229
auto clean = [] (JvmtiTagMapKey& entry, jlong tag) {
145-
entry.release_weak_handle();
230+
entry.release_handle();
146231
};
147232
_table.remove(jtme, clean);
148233
}
@@ -160,7 +245,7 @@ void JvmtiTagMapTable::remove_dead_entries(GrowableArray<jlong>* objects) {
160245
if (_objects != nullptr) {
161246
_objects->append(tag);
162247
}
163-
entry.release_weak_handle();
248+
entry.release_handle();
164249
return true;
165250
}
166251
return false;;

src/hotspot/share/prims/jvmtiTagMapTable.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,17 @@ class JvmtiTagMapKeyClosure;
4040
// This class is the Key type for inserting in ResizeableResourceHashTable
4141
// Its get_hash() and equals() methods are also used for getting the hash
4242
// value of a Key and comparing two Keys, respectively.
43+
//
44+
// Valhalla: Keep just one tag for all equal value objects including heap allocated value objects.
45+
// We have to keep a strong reference to each unique value object with a non-zero tag.
4346
class JvmtiTagMapKey : public CHeapObj<mtServiceability> {
44-
WeakHandle _wh;
47+
// All equal value objects should have the same tag.
48+
// Keep value objects alive (1 copy for each "value") until their tags are removed.
49+
union {
50+
WeakHandle _wh;
51+
OopHandle _h; // for value objects (_is_weak == false)
52+
};
53+
bool _is_weak;
4554
oop _obj; // temporarily hold obj while searching
4655
public:
4756
JvmtiTagMapKey(oop obj);
@@ -50,14 +59,10 @@ class JvmtiTagMapKey : public CHeapObj<mtServiceability> {
5059

5160
oop object() const;
5261
oop object_no_keepalive() const;
53-
void release_weak_handle();
62+
void release_handle();
5463

5564
static unsigned get_hash(const JvmtiTagMapKey& entry);
56-
static bool equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs) {
57-
oop lhs_obj = lhs._obj != nullptr ? lhs._obj : lhs.object_no_keepalive();
58-
oop rhs_obj = rhs._obj != nullptr ? rhs._obj : rhs.object_no_keepalive();
59-
return lhs_obj == rhs_obj;
60-
}
65+
static bool equals(const JvmtiTagMapKey& lhs, const JvmtiTagMapKey& rhs);
6166
};
6267

6368
typedef
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (c) 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+
* @test
26+
* @summary Tests SetTag/GetTag functionality for value objects.
27+
* @requires vm.jvmti
28+
* @modules java.base/jdk.internal.vm.annotation
29+
* @enablePreview
30+
* @run main/othervm/native -agentlib:ValueTagMapTest
31+
* -XX:+PrintInlineLayout
32+
* -XX:+PrintFlatArrayLayout
33+
* -Xlog:jvmti+table
34+
* ValueTagMapTest
35+
*/
36+
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
import java.util.concurrent.TimeUnit;
40+
import jdk.internal.vm.annotation.NullRestricted;
41+
import jdk.internal.vm.annotation.Strict;
42+
43+
public class ValueTagMapTest {
44+
45+
private static value class ValueClass {
46+
public int f;
47+
public ValueClass(int v) { f = v + 1; }
48+
public String toString() {
49+
return String.valueOf(f);
50+
}
51+
}
52+
53+
private static value class ValueHolder {
54+
@Strict
55+
@NullRestricted
56+
public ValueClass f0;
57+
58+
public ValueHolder(int v) {
59+
f0 = new ValueClass(v);
60+
}
61+
public String toString() {
62+
return "holder{" + f0 + "}";
63+
}
64+
}
65+
66+
private static value class ValueHolder2 {
67+
public ValueHolder f1;
68+
@Strict
69+
@NullRestricted
70+
public ValueHolder f2;
71+
72+
public ValueHolder2(int v, int v2) {
73+
f1 = new ValueHolder(v);
74+
f2 = new ValueHolder(v2);
75+
}
76+
public ValueHolder2(ValueHolder h1, int v2) {
77+
f1 = h1;
78+
f2 = new ValueHolder(v2);
79+
}
80+
public String toString() {
81+
return "holder2{" + f1 + ", " + f2 + "}";
82+
}
83+
}
84+
85+
86+
private static native void setTag0(Object object, long tag);
87+
private static void setTag(Object object, long tag) {
88+
setTag0(object, tag);
89+
}
90+
private static native long getTag0(Object object);
91+
private static long getTag(Object object) {
92+
long tag = getTag0(object);
93+
if (tag == 0) {
94+
throw new RuntimeException("Zero tag for object " + object);
95+
}
96+
return tag;
97+
}
98+
99+
private static void testGetTag(Object o1, Object o2) {
100+
long tag1 = getTag(o1);
101+
long tag2 = getTag(o2);
102+
if (o1 == o2) {
103+
if (tag1 != tag2) {
104+
throw new RuntimeException("different tags for equal objects: "
105+
+ o1 + " (tag " + tag1 + "), "
106+
+ o2 + " (tag " + tag2 + ")");
107+
}
108+
} else {
109+
if (tag1 == tag2) {
110+
throw new RuntimeException("equal tags for different objects: "
111+
+ o1 + " (tag " + tag1 + "), "
112+
+ o2 + " (tag " + tag2 + ")");
113+
}
114+
}
115+
}
116+
117+
public static void main(String[] args) {
118+
System.loadLibrary("ValueTagMapTest");
119+
List<ValueHolder2> items = new ArrayList<>();
120+
121+
for (int i = 0; i < 20; i++) {
122+
items.add(new ValueHolder2(i % 4, i % 8));
123+
}
124+
125+
long startTime = System.nanoTime();
126+
long tag = 1;
127+
for (ValueHolder2 item : items) {
128+
setTag(item, tag++);
129+
setTag(item.f1, tag++);
130+
setTag(item.f2, tag++);
131+
}
132+
133+
for (ValueHolder2 item: items) {
134+
long tag0 = getTag(item);
135+
long tag1 = getTag(item.f1);
136+
long tag2 = getTag(item.f2);
137+
System.out.println("getTag (" + item + "): " + tag0 + ", f1: " + tag1 + ", f2:" + tag2);
138+
}
139+
140+
startTime = System.nanoTime();
141+
for (ValueHolder2 item1: items) {
142+
for (ValueHolder2 item2 : items) {
143+
testGetTag(item1, item2);
144+
testGetTag(item1.f1, item2.f1);
145+
testGetTag(item1.f2, item2.f2);
146+
testGetTag(item1.f1, item2.f2);
147+
testGetTag(item1.f2, item2.f1);
148+
}
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)