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) {