Skip to content

Commit

Permalink
8304074: [JMX] Add an approximation of total bytes allocated on the J…
Browse files Browse the repository at this point in the history
…ava heap by the JVM

Reviewed-by: simonis, andrew
Backport-of: 3eced01f9efe2567a07b63343f8559683a2d0517
  • Loading branch information
Paul Hohensee committed Feb 26, 2024
1 parent d7ad5bf commit 1fad52d
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 82 deletions.
3 changes: 3 additions & 0 deletions hotspot/src/share/vm/runtime/mutexLocker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Mutex* Shared_SATB_Q_lock = NULL;
Mutex* DirtyCardQ_FL_lock = NULL;
Monitor* DirtyCardQ_CBL_mon = NULL;
Mutex* Shared_DirtyCardQ_lock = NULL;
Mutex* MonitoringSupport_lock = NULL;
Mutex* ParGCRareEvent_lock = NULL;
Mutex* EvacFailureStack_lock = NULL;
Mutex* DerivedPointerTableGC_lock = NULL;
Expand Down Expand Up @@ -208,6 +209,8 @@ void mutex_init() {

def(StringDedupQueue_lock , Monitor, leaf, true );
def(StringDedupTable_lock , Mutex , leaf, true );

def(MonitoringSupport_lock , Mutex , leaf, true ); // used for serviceability monitoring support
}
def(ParGCRareEvent_lock , Mutex , leaf , true );
def(DerivedPointerTableGC_lock , Mutex, leaf, true );
Expand Down
3 changes: 2 additions & 1 deletion hotspot/src/share/vm/runtime/mutexLocker.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, 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
Expand Down Expand Up @@ -101,6 +101,7 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card
// queue shared by
// non-Java threads.
// (see option ExplicitGCInvokesConcurrent)
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark.
extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops.
extern Mutex* EvacFailureStack_lock; // guards the evac failure scan stack
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
Expand Down
5 changes: 3 additions & 2 deletions hotspot/src/share/vm/services/jmm.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, 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
Expand Down Expand Up @@ -218,7 +218,8 @@ typedef struct {
} dcmdArgInfo;

typedef struct jmmInterface_1_ {
void* reserved1;
jlong (JNICALL *GetTotalThreadAllocatedMemory)
(JNIEnv *env);
jlong (JNICALL *GetOneThreadAllocatedMemory)
(JNIEnv *env,
jlong thread_id);
Expand Down
44 changes: 35 additions & 9 deletions hotspot/src/share/vm/services/management.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "runtime/interfaceSupport.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/thread.inline.hpp"
Expand Down Expand Up @@ -428,8 +429,6 @@ static MemoryPool* get_memory_pool_from_jobject(jobject obj, TRAPS) {
return MemoryService::get_memory_pool(ph);
}

#endif // INCLUDE_MANAGEMENT

static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
int num_threads = ids_ah->length();

Expand All @@ -445,8 +444,6 @@ static void validate_thread_id_array(typeArrayHandle ids_ah, TRAPS) {
}
}

#if INCLUDE_MANAGEMENT

static void validate_thread_info_array(objArrayHandle infoArray_h, TRAPS) {
// check if the element of infoArray is of type ThreadInfo class
Klass* threadinfo_klass = Management::java_lang_management_ThreadInfo_klass(CHECK);
Expand Down Expand Up @@ -2230,7 +2227,39 @@ jlong Management::ticks_to_ms(jlong ticks) {
return (jlong)(((double)ticks / (double)os::elapsed_frequency())
* (double)1000.0);
}
#endif // INCLUDE_MANAGEMENT

// Gets the amount of memory allocated on the Java heap since JVM launch.
JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env))
// We keep a high water mark to ensure monotonicity
static jlong high_water_result = 0;
static jlong prev_result = 0;

jlong result;
if (Threads_lock->try_lock()) {
result = ThreadService::exited_allocated_bytes();
for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
jlong size = thread->cooked_allocated_bytes();
result += size;
}
Threads_lock->unlock();
} else {
// Return the previous result if Threads_lock is locked
result = prev_result;
}

{
MutexLockerEx ml(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag);
if (result < high_water_result) {
// Result wrapped to a negative value, in which case it's
// pegged at the last positive value.
result = high_water_result;
} else {
high_water_result = result;
}
prev_result = result;
}
return result;
JVM_END

// Gets the amount of memory allocated on the Java heap for a single thread.
// Returns -1 if the thread does not exist or has terminated.
Expand Down Expand Up @@ -2368,11 +2397,8 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
}
JVM_END



#if INCLUDE_MANAGEMENT
const struct jmmInterface_1_ jmm_interface = {
NULL,
jmm_GetTotalThreadAllocatedMemory,
jmm_GetOneThreadAllocatedMemory,
jmm_GetVersion,
jmm_GetOptionalSupport,
Expand Down
5 changes: 5 additions & 0 deletions hotspot/src/share/vm/services/threadService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ PerfVariable* ThreadService::_daemon_threads_count = NULL;
volatile int ThreadService::_exiting_threads_count = 0;
volatile int ThreadService::_exiting_daemon_threads_count = 0;

volatile jlong ThreadService::_exited_allocated_bytes = 0;

ThreadDumpResult* ThreadService::_threaddump_list = NULL;

static const int INITIAL_ARRAY_SIZE = 10;
Expand Down Expand Up @@ -119,6 +121,9 @@ void ThreadService::add_thread(JavaThread* thread, bool daemon) {
}

void ThreadService::remove_thread(JavaThread* thread, bool daemon) {
// Include hidden thread allcations in exited_allocated_bytes
ThreadService::incr_exited_allocated_bytes(thread->cooked_allocated_bytes());

Atomic::dec((jint*) &_exiting_threads_count);

if (thread->is_hidden_from_external_view() ||
Expand Down
10 changes: 10 additions & 0 deletions hotspot/src/share/vm/services/threadService.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class ThreadService : public AllStatic {
static PerfVariable* _peak_threads_count;
static PerfVariable* _daemon_threads_count;

static volatile jlong _exited_allocated_bytes;

// These 2 counters are atomically incremented once the thread is exiting.
// They will be atomically decremented when ThreadService::remove_thread is called.
static volatile int _exiting_threads_count;
Expand Down Expand Up @@ -95,6 +97,14 @@ class ThreadService : public AllStatic {
static int exiting_threads_count() { return _exiting_threads_count; }
static int exiting_daemon_threads_count() { return _exiting_daemon_threads_count; }

static jlong exited_allocated_bytes() { return Atomic::load(&_exited_allocated_bytes); }
static void incr_exited_allocated_bytes(jlong size) {
// No need for an atomic add because called under the Threads_lock,
// but because _exited_allocated_bytes is read concurrently, need
// atomic store to avoid readers seeing a partial update.
Atomic::store(_exited_allocated_bytes + size, &_exited_allocated_bytes);
}

// Support for thread dump
static void add_thread_dump(ThreadDumpResult* dump);
static void remove_thread_dump(ThreadDumpResult* dump);
Expand Down
1 change: 1 addition & 0 deletions jdk/make/mapfiles/libmanagement/mapfile-vers
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ SUNWprivate_1.1 {
Java_sun_management_ThreadImpl_getThreadUserCpuTime1;
Java_sun_management_ThreadImpl_getThreadAllocatedMemory0;
Java_sun_management_ThreadImpl_getThreadAllocatedMemory1;
Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory;
Java_sun_management_ThreadImpl_resetContentionTimes0;
Java_sun_management_ThreadImpl_resetPeakThreadCount0;
Java_sun_management_ThreadImpl_setThreadContentionMonitoringEnabled0;
Expand Down
36 changes: 36 additions & 0 deletions jdk/src/share/classes/com/sun/management/ThreadMXBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,42 @@ public interface ThreadMXBean extends java.lang.management.ThreadMXBean {
*/
public long[] getThreadUserTime(long[] ids);

/**
* Returns an approximation of the total amount of memory, in bytes, allocated
* in heap memory by all threads since the Java virtual machine started.
* The returned value is an approximation because some Java virtual machine
* implementations may use object allocation mechanisms that result in a
* delay between the time an object is allocated and the time its size is
* recorded.
*
* @implSpec The default implementation throws {@code UnsupportedOperationException}
* if the Java virtual machine implementation does not support thread
* memory allocation measurement, and otherwise acts as though thread
* memory allocation measurement is disabled.
*
* @return an approximation of the total memory allocated, in bytes, in
* heap memory since the Java virtual machine was started,
* if thread memory allocation measurement is enabled;
* {@code -1} otherwise.
*
* @throws UnsupportedOperationException if the Java virtual
* machine implementation does not support thread memory allocation
* measurement.
*
* @see #isThreadAllocatedMemorySupported
* @see #isThreadAllocatedMemoryEnabled
* @see #setThreadAllocatedMemoryEnabled
*
* @since 8u412
*/
public default long getTotalThreadAllocatedBytes() {
if (!isThreadAllocatedMemorySupported()) {
throw new UnsupportedOperationException(
"Thread allocated memory measurement is not supported.");
}
return -1;
}

/**
* Returns an approximation of the total amount of memory, in bytes,
* allocated in heap memory for the current thread.
Expand Down
8 changes: 8 additions & 0 deletions jdk/src/share/classes/sun/management/ThreadImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ public void setThreadCpuTimeEnabled(boolean enable) {
}
}

public long getTotalThreadAllocatedBytes() {
if (isThreadAllocatedMemoryEnabled()) {
return getTotalThreadAllocatedMemory();
}
return -1;
}

public long getCurrentThreadAllocatedBytes() {
if (isThreadAllocatedMemoryEnabled()) {
return getThreadAllocatedMemory0(0);
Expand Down Expand Up @@ -507,6 +514,7 @@ private static native void getThreadInfo1(long[] ids,
private static native void getThreadUserCpuTime1(long[] ids, long[] result);
private static native long getThreadAllocatedMemory0(long id);
private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
private static native long getTotalThreadAllocatedMemory();
private static native void setThreadCpuTimeEnabled0(boolean enable);
private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
private static native void setThreadContentionMonitoringEnabled0(boolean enable);
Expand Down
5 changes: 3 additions & 2 deletions jdk/src/share/javavm/export/jmm.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, 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
Expand Down Expand Up @@ -218,7 +218,8 @@ typedef struct {
} dcmdArgInfo;

typedef struct jmmInterface_1_ {
void* reserved1;
jlong (JNICALL *GetTotalThreadAllocatedMemory)
(JNIEnv *env);
jlong (JNICALL *GetOneThreadAllocatedMemory)
(JNIEnv *env,
jlong thread_id);
Expand Down
11 changes: 9 additions & 2 deletions jdk/src/share/native/sun/management/ThreadImpl.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, 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
Expand Down Expand Up @@ -98,7 +98,7 @@ JNIEXPORT jlong JNICALL
Java_sun_management_ThreadImpl_getThreadAllocatedMemory0
(JNIEnv *env, jclass cls, jlong tid)
{
return jmm_interface->GetOneThreadAllocatedMemory(env, tid);
return jmm_interface->GetOneThreadAllocatedMemory(env, tid);
}

JNIEXPORT void JNICALL
Expand All @@ -108,6 +108,13 @@ Java_sun_management_ThreadImpl_getThreadAllocatedMemory1
jmm_interface->GetThreadAllocatedMemory(env, ids, sizeArray);
}

JNIEXPORT jlong JNICALL
Java_sun_management_ThreadImpl_getTotalThreadAllocatedMemory
(JNIEnv *env, jclass cls)
{
return jmm_interface->GetTotalThreadAllocatedMemory(env);
}

JNIEXPORT jobjectArray JNICALL
Java_sun_management_ThreadImpl_findMonitorDeadlockedThreads0
(JNIEnv *env, jclass cls)
Expand Down
Loading

0 comments on commit 1fad52d

Please sign in to comment.