diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml
index f8d5a52d10b..9ead8a21952 100644
--- a/src/hotspot/share/prims/jvmti.xml
+++ b/src/hotspot/share/prims/jvmti.xml
@@ -10710,6 +10710,15 @@ myInit() {
.
+
+
+ Can support value objects.
+ When preview features are enabled and this capability is enabled, then
+ events are sent for value objects when the following two events are enabled:
+ ,
+ .
+
+
@@ -14013,9 +14022,11 @@ myInit() {
VM. These methods should send this event.
Virtual machines which are incapable of bytecode instrumentation
for some or all of their methods can send this event.
- When preview features are enabled and the allocated object has no identity,
- null is passed for the parameter.
-
+
+ When preview features are enabled and the capability can_support_value_objects
+ is enabled, this event is sent for the value object allocations.
+ null is passed for the parameter in such cases.
+
Note that the SampledObjectAlloc
event is triggered on all Java object allocations, including those
@@ -14041,6 +14052,7 @@ myInit() {
new
+
@@ -14061,7 +14073,8 @@ myInit() {
JNI local reference to the object that was allocated.
- Null when preview features are enabled and the allocated object has no identity.
+ Null when preview features are enabled, the capability can_support_value_objects
+ is enabled, and the allocated object has no identity.
@@ -14108,13 +14121,15 @@ myInit() {
In conjunction with weak references and the function
, a user can track which objects were allocated from which
stack trace, and which are still live during the execution of the program.
- When preview features are enabled and the allocated object has no identity,
- null is passed for the parameter.
- Note that weak references support identity objects only.
+
+ When preview features are enabled and the capability can_support_value_objects
+ is enabled, this event is sent for the allocated objects that have no identity.
+ null is passed for the parameter in such cases.
new
+
@@ -14135,7 +14150,8 @@ myInit() {
JNI local reference to the object that was allocated.
- Null when preview features are enabled and the allocated object has no identity.
+ Null when preview features are enabled, the capability can_support_value_objects
+ is enabled, and the allocated object has no identity.
@@ -15503,6 +15519,12 @@ typedef void (JNICALL *jvmtiEventVMInit)
Add new function ClearAllFramePops. Needed to speedup debugger single stepping.
+
+ Support for value objects (Preview):
+ Add new capability: can_support_value_objects which enables
+ the following events for value objects:
+ JVMTI_EVENT_VM_OBJECT_ALLOC, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC.
+
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
index af3bbbcd212..d5ce83ab61a 100644
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -1282,6 +1282,7 @@ bool JvmtiExport::_can_post_frame_pop = fals
bool JvmtiExport::_can_pop_frame = false;
bool JvmtiExport::_can_force_early_return = false;
bool JvmtiExport::_can_support_virtual_threads = false;
+bool JvmtiExport::_can_support_value_objects = false;
bool JvmtiExport::_can_get_owned_monitor_info = false;
bool JvmtiExport::_early_vmstart_recorded = false;
@@ -2967,6 +2968,10 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
if (thread->should_hide_jvmti_events()) {
return;
}
+ const bool is_inline = object->is_inline();
+ if (is_inline && !JvmtiExport::can_support_value_objects()) {
+ return;
+ }
HandleMark hm(thread);
Handle h(thread, object);
@@ -2974,10 +2979,11 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != nullptr; env = it.next(env)) {
- if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+ if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC) &&
+ (!is_inline || env->get_capabilities()->can_support_value_objects != 0)) {
EVT_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Evt vmobject alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
- object==nullptr? "null" : object->klass()->external_name()));
+ object->klass()->external_name()));
JvmtiObjectAllocEventMark jem(thread, h());
JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread)
@@ -3004,19 +3010,24 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
if (thread->should_hide_jvmti_events()) {
return;
}
+ const bool is_inline = object->is_inline();
+ if (is_inline && !JvmtiExport::can_support_value_objects()) {
+ return;
+ }
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Trg sampled object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
JvmtiEnvThreadStateIterator it(state);
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
- if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
+ JvmtiEnv *env = ets->get_env();
+ if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) &&
+ (!is_inline || env->get_capabilities()->can_support_value_objects != 0)) {
EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Evt sampled object alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
- object == nullptr ? "null" : object->klass()->external_name()));
+ object->klass()->external_name()));
- JvmtiEnv *env = ets->get_env();
JvmtiObjectAllocEventMark jem(thread, h());
JVMTI_JAVA_THREAD_EVENT_CALLBACK_BLOCK(thread)
jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp
index 086667ae72d..24992085473 100644
--- a/src/hotspot/share/prims/jvmtiExport.hpp
+++ b/src/hotspot/share/prims/jvmtiExport.hpp
@@ -95,6 +95,7 @@ class JvmtiExport : public AllStatic {
JVMTI_SUPPORT_FLAG(can_pop_frame)
JVMTI_SUPPORT_FLAG(can_force_early_return)
JVMTI_SUPPORT_FLAG(can_support_virtual_threads)
+ JVMTI_SUPPORT_FLAG(can_support_value_objects)
JVMTI_SUPPORT_FLAG(early_vmstart_recorded)
JVMTI_SUPPORT_FLAG(can_get_owned_monitor_info) // includes can_get_owned_monitor_stack_depth_info
diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
index b18e58ca1e5..e54099d6ad7 100644
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
+++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,6 +56,7 @@ jvmtiCapabilities JvmtiManageCapabilities::onload_solo_remaining_capabilities;
jvmtiCapabilities JvmtiManageCapabilities::acquired_capabilities;
int JvmtiManageCapabilities::_can_support_virtual_threads_count = 0;
+int JvmtiManageCapabilities::_can_support_value_objects_count = 0;
Mutex* JvmtiManageCapabilities::_capabilities_lock = nullptr;
@@ -103,6 +104,7 @@ jvmtiCapabilities JvmtiManageCapabilities::init_always_capabilities() {
jc.can_generate_resource_exhaustion_heap_events = 1;
jc.can_generate_resource_exhaustion_threads_events = 1;
jc.can_support_virtual_threads = 1;
+ jc.can_support_value_objects = 1;
return jc;
}
@@ -277,6 +279,9 @@ jvmtiError JvmtiManageCapabilities::add_capabilities(const jvmtiCapabilities *cu
if (desired->can_support_virtual_threads != 0 && current->can_support_virtual_threads == 0) {
_can_support_virtual_threads_count++;
}
+ if (desired->can_support_value_objects != 0 && current->can_support_value_objects == 0) {
+ _can_support_value_objects_count++;
+ }
// return the result
either(current, desired, result);
@@ -309,6 +314,11 @@ void JvmtiManageCapabilities::relinquish_capabilities(const jvmtiCapabilities *c
assert(_can_support_virtual_threads_count > 0, "sanity check");
_can_support_virtual_threads_count--;
}
+ if (to_trash.can_support_value_objects != 0) {
+ assert(current->can_support_value_objects != 0, "sanity check");
+ assert(_can_support_value_objects_count > 0, "sanity check");
+ _can_support_value_objects_count--;
+ }
update();
@@ -393,6 +403,7 @@ void JvmtiManageCapabilities::update() {
JvmtiExport::set_can_pop_frame(avail.can_pop_frame);
JvmtiExport::set_can_force_early_return(avail.can_force_early_return);
JvmtiExport::set_can_support_virtual_threads(_can_support_virtual_threads_count != 0);
+ JvmtiExport::set_can_support_value_objects(_can_support_value_objects_count != 0);
JvmtiExport::set_should_clean_up_heap_objects(avail.can_generate_breakpoint_events);
JvmtiExport::set_can_get_owned_monitor_info(avail.can_get_owned_monitor_info ||
avail.can_get_owned_monitor_stack_depth_info);
@@ -488,6 +499,8 @@ void JvmtiManageCapabilities:: print(const jvmtiCapabilities* cap) {
log_trace(jvmti)("can_generate_early_class_hook_events");
if (cap->can_support_virtual_threads)
log_trace(jvmti)("can_support_virtual_threads");
+ if (cap->can_support_value_objects)
+ log_trace(jvmti)("can_support_value_objects");
}
#endif
diff --git a/src/hotspot/share/prims/jvmtiManageCapabilities.hpp b/src/hotspot/share/prims/jvmtiManageCapabilities.hpp
index 6bf6830c479..61e029b7781 100644
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.hpp
+++ b/src/hotspot/share/prims/jvmtiManageCapabilities.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -51,6 +51,9 @@ class JvmtiManageCapabilities : public AllStatic {
// counter for the agents possess can_support_virtual_threads capability
static int _can_support_virtual_threads_count;
+ // counter for the agents possess can_support_value_objects capability
+ static int _can_support_value_objects_count;
+
// lock to access the class data
static Mutex* _capabilities_lock;
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.cpp
index 87d0970d8cb..33b20454654 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.cpp
+++ b/test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.cpp
@@ -529,11 +529,6 @@ static void event_storage_add(EventStorage* storage,
jint count;
jvmtiError err;
- if (!jni->HasIdentity(object)) {
- // weak references are prohibited for non-identity objects, skip them
- return;
- }
-
err = jvmti->GetStackTrace(thread, 0, 64, frames, &count);
if (err == JVMTI_ERROR_NONE && count >= 1) {
ObjectTrace* live_object;
diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/SampledObjectAllocValue.java b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/SampledObjectAllocValue.java
index 5bd28640d1d..11c12cdfa96 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/SampledObjectAllocValue.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/SampledObjectAllocValue.java
@@ -29,6 +29,7 @@
*
* @enablePreview
* @run main/othervm/native -agentlib:SampledObjectAllocValue SampledObjectAllocValue
+ * @run main/othervm/native -agentlib:SampledObjectAllocValue=can_support_value_objects SampledObjectAllocValue
*/
public class SampledObjectAllocValue {
diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/libSampledObjectAllocValue.cpp b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/libSampledObjectAllocValue.cpp
index bd6a3e151f3..5511de5b8c3 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/libSampledObjectAllocValue.cpp
+++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/SampledObjectAllocValue/libSampledObjectAllocValue.cpp
@@ -21,6 +21,7 @@
* questions.
*/
+#include
#include "jvmti.h"
#include "jvmti_common.hpp"
@@ -31,6 +32,7 @@ extern "C" {
static std::atomic events_counter(0);
static jvmtiEnv* jvmti;
static jclass expected_class;
+static bool expect_events = false;
static bool
is_test_class(jvmtiEnv* jvmti, JNIEnv* jni, jclass cls) {
@@ -78,13 +80,18 @@ VMDeath(jvmtiEnv* jvmti, JNIEnv* jni) {
jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr);
check_jvmti_error(err, "SetEventNotificationMode for SAMPLED_OBJECT_ALLOC");
- if (events_counter == 0) {
+ LOG("VMDeath: expect_events: %d events_counter: %d\n", expect_events, events_counter.load());
+
+ if (expect_events && events_counter == 0) {
fatal(jni, "SampledObjectAlloc events counter shouldn't be zero");
}
- LOG("VMDeath: events_counter: %d\n", events_counter.load());
+ if (!expect_events && events_counter != 0) {
+ fatal(jni, "SampledObjectAlloc events counter should be zero");
+ }
}
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+ bool support_value_objects = options != nullptr && strcmp(options, "can_support_value_objects") == 0;
jvmtiCapabilities caps;
jvmtiEventCallbacks callbacks;
jvmtiError err;
@@ -99,6 +106,11 @@ jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
memset(&caps, 0, sizeof(caps));
caps.can_generate_sampled_object_alloc_events = 1;
+ if (support_value_objects) {
+ caps.can_support_value_objects = 1;
+ expect_events = true;
+ }
+
if (jvmti->AddCapabilities(&caps) != JVMTI_ERROR_NONE) {
return JNI_ERR;
}
@@ -110,7 +122,7 @@ jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
check_jvmti_error(err, "SetEventCallbacks");
- // Interval should be small enough to triggger sampling event.
+ // Interval should be small enough to trigger sampling event.
err = jvmti->SetHeapSamplingInterval(100);
check_jvmti_error(err, "SetHeapSamplingInterval");
diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/VMObjectAllocValueTest.java b/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/VMObjectAllocValueTest.java
index 3b31f5dafcc..32052279efe 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/VMObjectAllocValueTest.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/VMObjectAllocValueTest.java
@@ -26,7 +26,8 @@
* @summary Verifies that a VMObjectAlloc event is generated for a value object created using MethodHandle
* @requires vm.jvmti
* @enablePreview
- * @run main/othervm/native -agentlib:VMObjectAllocValueTest VMObjectAllocValueTest
+ * @run main/othervm/native -agentlib:VMObjectAllocValueTest VMObjectAllocValueTest 0
+ * @run main/othervm/native -agentlib:VMObjectAllocValueTest=can_support_value_objects VMObjectAllocValueTest 1
*/
import java.lang.invoke.MethodHandle;
@@ -47,8 +48,11 @@ public static void main(String[] args) throws Throwable {
MethodHandle mh = publicLookup.findConstructor(VMObjectAllocValueTest.class, mt);
mh.invoke("str"); // to trigger a VMObjectAlloc event invoke ctor not from Java
- if (getNumberOfAllocation() != 1) {
- throw new Exception("Number of allocation != 1");
+ int expected = Integer.parseInt(args[0]);
+ int actual = getNumberOfAllocation();
+
+ if (actual != expected) {
+ throw new Exception("Number of allocation: expected: " + expected + ", got: " + actual);
}
}
}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/libVMObjectAllocValueTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/libVMObjectAllocValueTest.cpp
index 682d6de0fbb..c5b5f807e0e 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/libVMObjectAllocValueTest.cpp
+++ b/test/hotspot/jtreg/serviceability/jvmti/valhalla/VMObjectAllocValue/libVMObjectAllocValueTest.cpp
@@ -71,6 +71,7 @@ Java_VMObjectAllocValueTest_getNumberOfAllocation(JNIEnv *env, jclass cls) {
extern JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+ bool support_value_objects = options != nullptr && strcmp(options, "can_support_value_objects") == 0;
jvmtiEventCallbacks callbacks;
jvmtiError err;
jvmtiCapabilities caps;
@@ -90,6 +91,9 @@ Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
callbacks.VMDeath = &VMDeath;
memset(&caps, 0, sizeof(caps));
caps.can_generate_vm_object_alloc_events = 1;
+ if (support_value_objects) {
+ caps.can_support_value_objects = 1;
+ }
err = jvmti->AddCapabilities( &caps);
if (err != JVMTI_ERROR_NONE) {