Skip to content
Closed
9 changes: 9 additions & 0 deletions src/hotspot/share/jfr/metadata/metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@
<Field type="InflateCause" name="cause" label="Monitor Inflation Cause" description="Cause of inflation" />
</Event>

<Event name="JavaMonitorDeflate" category="Java Application" label="Java Monitor Deflated">
<Field type="Class" name="monitorClass" label="Monitor Class" />
<Field type="ulong" contentType="address" name="address" label="Monitor Address" relation="JavaMonitorAddress" />
</Event>

<Event name="JavaMonitorStatistics" category="Java Application, Statistics" label="Java Monitor Statistics" period="everyChunk">
<Field type="ulong" name="count" label="Monitors in Use" description="Current number of in-use monitors" />
</Event>

<Event name="SyncOnValueBasedClass" category="Java Virtual Machine, Diagnostics" label="Value Based Class Synchronization" thread="true" stackTrace="true" startTime="false" experimental="true">
<Field type="Class" name="valueBasedClass" label="Value Based Class" />
</Event>
Expand Down
6 changes: 6 additions & 0 deletions src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,3 +739,9 @@ TRACE_REQUEST_FUNC(NativeMemoryUsage) {
TRACE_REQUEST_FUNC(NativeMemoryUsageTotal) {
JfrNativeMemoryEvent::send_total_event(timestamp());
}

TRACE_REQUEST_FUNC(JavaMonitorStatistics) {
EventJavaMonitorStatistics event;
event.set_count(ObjectSynchronizer::in_use_list_count());
event.commit();
}
6 changes: 5 additions & 1 deletion src/hotspot/share/runtime/lightweightSynchronizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,11 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event,
const oop obj,
ObjectSynchronizer::InflateCause cause) {
assert(event != nullptr, "invariant");
event->set_monitorClass(obj->klass());
const Klass* monitor_klass = obj->klass();
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
return;
}
event->set_monitorClass(monitor_klass);
event->set_address((uintptr_t)(void*)obj);
event->set_cause((u1)cause);
event->commit();
Expand Down
26 changes: 19 additions & 7 deletions src/hotspot/share/runtime/objectMonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,18 @@ bool ObjectMonitor::try_lock_or_add_to_entry_list(JavaThread* current, ObjectWai
}
}

static void post_monitor_deflate_event(EventJavaMonitorDeflate* event,
const oop obj) {
assert(event != nullptr, "invariant");
const Klass* monitor_klass = obj->klass();
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
return;
}
event->set_monitorClass(monitor_klass);
event->set_address((uintptr_t)(void*)obj);
event->commit();
}

// Deflate the specified ObjectMonitor if not in-use. Returns true if it
// was deflated and false otherwise.
//
Expand All @@ -753,6 +765,8 @@ bool ObjectMonitor::deflate_monitor(Thread* current) {
return false;
}

EventJavaMonitorDeflate event;

const oop obj = object_peek();

if (obj == nullptr) {
Expand Down Expand Up @@ -826,6 +840,10 @@ bool ObjectMonitor::deflate_monitor(Thread* current) {
install_displaced_markword_in_object(obj);
}

if (event.should_commit()) {
post_monitor_deflate_event(&event, obj);
}

// We leave owner == DEFLATER_MARKER and contentions < 0
// to force any racing threads to retry.
return true; // Success, ObjectMonitor has been deflated.
Expand Down Expand Up @@ -1628,12 +1646,6 @@ bool ObjectMonitor::check_owner(TRAPS) {
"current thread is not owner", false);
}

static inline bool is_excluded(const Klass* monitor_klass) {
assert(monitor_klass != nullptr, "invariant");
NOT_JFR_RETURN_(false);
JFR_ONLY(return vmSymbols::jdk_jfr_internal_management_HiddenWait() == monitor_klass->name();)
}

static void post_monitor_wait_event(EventJavaMonitorWait* event,
ObjectMonitor* monitor,
uint64_t notifier_tid,
Expand All @@ -1642,7 +1654,7 @@ static void post_monitor_wait_event(EventJavaMonitorWait* event,
assert(event != nullptr, "invariant");
assert(monitor != nullptr, "invariant");
const Klass* monitor_klass = monitor->object()->klass();
if (is_excluded(monitor_klass)) {
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
return;
}
event->set_monitorClass(monitor_klass);
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/runtime/objectMonitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
bool deflate_monitor(Thread* current);
private:
void install_displaced_markword_in_object(const oop obj);

// JFR support
public:
static bool is_jfr_excluded(const Klass* monitor_klass);
};

// RAII object to ensure that ObjectMonitor::is_being_async_deflated() is
Expand Down
7 changes: 7 additions & 0 deletions src/hotspot/share/runtime/objectMonitor.inline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "runtime/objectMonitor.hpp"

#include "classfile/vmSymbols.hpp"
#include "logging/log.hpp"
#include "oops/access.inline.hpp"
#include "oops/markWord.hpp"
Expand Down Expand Up @@ -286,4 +287,10 @@ inline bool ObjectMonitor::object_refers_to(oop obj) const {
return _object.peek() == obj;
}

inline bool ObjectMonitor::is_jfr_excluded(const Klass* monitor_klass) {
assert(monitor_klass != nullptr, "invariant");
NOT_JFR_RETURN_(false);
JFR_ONLY(return vmSymbols::jdk_jfr_internal_management_HiddenWait() == monitor_klass->name();)
}

#endif // SHARE_RUNTIME_OBJECTMONITOR_INLINE_HPP
18 changes: 15 additions & 3 deletions src/hotspot/share/runtime/synchronizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,14 @@ static bool monitors_used_above_threshold(MonitorList* list) {
return false;
}

size_t ObjectSynchronizer::in_use_list_count() {
return _in_use_list.count();
}

size_t ObjectSynchronizer::in_use_list_max() {
return _in_use_list.max();
}

size_t ObjectSynchronizer::in_use_list_ceiling() {
return _in_use_list_ceiling;
}
Expand Down Expand Up @@ -1419,7 +1427,11 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event,
const oop obj,
ObjectSynchronizer::InflateCause cause) {
assert(event != nullptr, "invariant");
event->set_monitorClass(obj->klass());
const Klass* monitor_klass = obj->klass();
if (ObjectMonitor::is_jfr_excluded(monitor_klass)) {
return;
}
event->set_monitorClass(monitor_klass);
event->set_address((uintptr_t)(void*)obj);
event->set_cause((u1)cause);
event->commit();
Expand Down Expand Up @@ -1710,8 +1722,8 @@ class ObjectMonitorDeflationLogging: public StackObj {
elapsedTimer _timer;

size_t ceiling() const { return ObjectSynchronizer::in_use_list_ceiling(); }
size_t count() const { return ObjectSynchronizer::_in_use_list.count(); }
size_t max() const { return ObjectSynchronizer::_in_use_list.max(); }
size_t count() const { return ObjectSynchronizer::in_use_list_count(); }
size_t max() const { return ObjectSynchronizer::in_use_list_max(); }

public:
ObjectMonitorDeflationLogging()
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/runtime/synchronizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ class ObjectSynchronizer : AllStatic {

// Deflate idle monitors:
static size_t deflate_monitor_list(ObjectMonitorDeflationSafepointer* safepointer);
static size_t in_use_list_count();
static size_t in_use_list_max();
static size_t in_use_list_ceiling();
static void dec_in_use_list_ceiling();
static void inc_in_use_list_ceiling();
Expand Down
10 changes: 10 additions & 0 deletions src/jdk.jfr/share/conf/jfr/default.jfc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@
<setting name="threshold">0 ms</setting>
</event>

<event name="jdk.JavaMonitorDeflate">
<setting name="enabled">false</setting>
<setting name="threshold">0 ms</setting>
</event>

<event name="jdk.JavaMonitorStatistics">
<setting name="enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>

<event name="jdk.SyncOnValueBasedClass">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
Expand Down
10 changes: 10 additions & 0 deletions src/jdk.jfr/share/conf/jfr/profile.jfc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@
<setting name="threshold">0 ms</setting>
</event>

<event name="jdk.JavaMonitorDeflate">
<setting name="enabled">false</setting>
<setting name="threshold">0 ms</setting>
</event>

<event name="jdk.JavaMonitorStatistics">
<setting name="enabled">true</setting>
<setting name="period">everyChunk</setting>
</event>

<event name="jdk.SyncOnValueBasedClass">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
Expand Down
90 changes: 90 additions & 0 deletions test/jdk/jdk/jfr/event/runtime/TestJavaMonitorDeflateEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2016, 2025, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package jdk.jfr.event.runtime;

import static jdk.test.lib.Asserts.assertFalse;

import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
import jdk.test.lib.thread.TestThread;
import jdk.test.lib.thread.XRun;

/**
* @test TestJavaMonitorDeflateEvent
* @requires vm.flagless
* @requires vm.hasJFR
* @library /test/lib
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedAsyncDeflationInterval=100 jdk.jfr.event.runtime.TestJavaMonitorDeflateEvent
*/
public class TestJavaMonitorDeflateEvent {

private static final String FIELD_KLASS_NAME = "monitorClass.name";
private static final String FIELD_ADDRESS = "address";

private static final String EVENT_NAME = EventNames.JavaMonitorDeflate;

static class Lock {
}

public static void main(String[] args) throws Exception {
final Lock lock = new Lock();
final String lockClassName = lock.getClass().getName().replace('.', '/');

List<RecordedEvent> events = new CopyOnWriteArrayList<>();
try (RecordingStream rs = new RecordingStream()) {
rs.enable(EVENT_NAME).withoutThreshold();
rs.onEvent(EVENT_NAME, e -> {
Object clazz = e.getValue(FIELD_KLASS_NAME);
if (clazz.equals(lockClassName)) {
events.add(e);
rs.close();
}
});
rs.startAsync();

synchronized (lock) {
// Causes lock inflation.
lock.wait(1);
}

// Wait for deflater thread to act.
rs.awaitTermination();

System.out.println(events);
assertFalse(events.isEmpty());
for (RecordedEvent ev : events) {
Events.assertField(ev, FIELD_ADDRESS).notEqual(0L);
}
}
}
}
Loading