Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions runtime/gc/gctable.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ J9MemoryManagerFunctions MemoryManagerFunctions = {
j9gc_get_softmx,
j9gc_get_initial_heap_size,
j9gc_get_maximum_heap_size,
j9gc_get_tlh_minimum_size,
j9gc_get_tlh_initial_size,
j9gc_is_tlab_enabled,
j9gc_get_minimum_young_generation_size,
j9gc_get_maximum_young_generation_size,
j9gc_objaccess_checkClassLive,
Expand Down
3 changes: 3 additions & 0 deletions runtime/gc_base/gc_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ extern J9_CFUNC jint JNICALL queryGCStatus(JavaVM *vm, jint *nHeaps, GCStatus *
extern J9_CFUNC int j9gc_finalizer_startup(J9JavaVM * vm);
extern J9_CFUNC UDATA j9gc_wait_for_reference_processing(J9JavaVM *vm);
extern J9_CFUNC UDATA j9gc_get_maximum_heap_size(J9JavaVM *javaVM);
extern J9_CFUNC UDATA j9gc_get_tlh_minimum_size(J9JavaVM *javaVM);
extern J9_CFUNC UDATA j9gc_get_tlh_initial_size(J9JavaVM *javaVM);
extern J9_CFUNC BOOLEAN j9gc_is_tlab_enabled(J9JavaVM *javaVM);
extern J9_CFUNC void* j9gc_get_heap_base(J9JavaVM *javaVM);
extern J9_CFUNC void* j9gc_get_heap_top(J9JavaVM *javaVM);
extern J9_CFUNC UDATA j9gc_get_minimum_young_generation_size(J9JavaVM *javaVM);
Expand Down
29 changes: 29 additions & 0 deletions runtime/gc_base/modronapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,35 @@ j9gc_get_maximum_heap_size(J9JavaVM *javaVM)
return size;
}

/**
* API to return the minimum TLH (Thread Local Heap) size.
*/
UDATA
j9gc_get_tlh_minimum_size(J9JavaVM *javaVM)
{
return MM_GCExtensions::getExtensions(javaVM)->tlhMinimumSize;
}

/**
* API to return the initial TLH (Thread Local Heap) size.
*/
UDATA
j9gc_get_tlh_initial_size(J9JavaVM *javaVM)
{
return MM_GCExtensions::getExtensions(javaVM)->tlhInitialSize;
}

/**
* API to return whether TLH allocation is enabled.
*/
BOOLEAN
j9gc_is_tlab_enabled(J9JavaVM *javaVM)
{
uintptr_t allocType = 0;
UDATA found = javaVM->memoryManagerFunctions->j9gc_modron_getConfigurationValueForKey(javaVM, j9gc_modron_configuration_allocationType, &allocType);
return (found && (j9gc_modron_allocation_type_tlh == allocType)) ? TRUE : FALSE;
}

/**
* API to return the heap base address
*/
Expand Down
3 changes: 3 additions & 0 deletions runtime/gc_base/modronapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ UDATA j9gc_set_softmx(J9JavaVM *javaVM, UDATA newsoftMx);
UDATA j9gc_get_softmx(J9JavaVM *javaVM);
UDATA j9gc_get_initial_heap_size(J9JavaVM *javaVM);
UDATA j9gc_get_maximum_heap_size(J9JavaVM *javaVM);
UDATA j9gc_get_tlh_minimum_size(J9JavaVM *javaVM);
UDATA j9gc_get_tlh_initial_size(J9JavaVM *javaVM);
BOOLEAN j9gc_is_tlab_enabled(J9JavaVM *javaVM);
void *j9gc_get_heap_base(J9JavaVM *javaVM);
void *j9gc_get_heap_top(J9JavaVM *javaVM);
UDATA j9gc_get_minimum_young_generation_size(J9JavaVM *javaVM);
Expand Down
1 change: 1 addition & 0 deletions runtime/oti/j9consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,7 @@ extern "C" {
#define J9JFR_EVENT_TYPE_NATIVE_LIBRARY 32
#define J9JFR_EVENT_TYPE_THREAD_DUMP 33
#define J9JFR_EVENT_TYPE_THREAD_ALLOCATION_STATISTICS 34
#define J9JFR_EVENT_TYPE_GC_TLAB_CONFIGURATION 36

/* JFR thread states. */

Expand Down
10 changes: 10 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,13 @@ typedef struct J9JFRThreadAllocationStatistics {
U_64 allocated;
} J9JFRThreadAllocationStatistics;

typedef struct J9JFRGCTLABConfiguration {
J9JFR_EVENT_COMMON_FIELDS
BOOLEAN usesTLABs;
U_64 minTLABSize;
U_64 tlabRefillWasteLimit;
} J9JFRGCTLABConfiguration;

#endif /* defined(J9VM_OPT_JFR) */

/* @ddr_namespace: map_to_type=J9CfrError */
Expand Down Expand Up @@ -5044,6 +5051,9 @@ typedef struct J9MemoryManagerFunctions {
UDATA ( *j9gc_get_softmx)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_initial_heap_size)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_maximum_heap_size)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_tlh_minimum_size)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_tlh_initial_size)(struct J9JavaVM *javaVM) ;
BOOLEAN ( *j9gc_is_tlab_enabled)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_minimum_young_generation_size)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_get_maximum_young_generation_size)(struct J9JavaVM *javaVM) ;
UDATA ( *j9gc_objaccess_checkClassLive)(struct J9JavaVM *javaVM, J9Class *classPtr) ;
Expand Down
27 changes: 27 additions & 0 deletions runtime/vm/JFRChunkWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,33 @@ VM_JFRChunkWriter::writeYoungGenerationConfigurationEvent()
writeEventSize(dataStart);
}

void
VM_JFRChunkWriter::writeGCTLABConfigurationEvent()
{
GCTLABConfigurationEntry *tlabConfig = &(VM_JFRConstantPoolTypes::getJFRConstantEvents(_vm)->GCTLABConfigEntry);

/* reserve size field */
U_8 *dataStart = reserveEventSize();

/* write event type */
_bufferWriter->writeLEB128(GCTLABConfigurationID);

/* write event start time */
_bufferWriter->writeLEB128(j9time_nano_time());

/* write whether TLABs are in use */
_bufferWriter->writeBoolean(tlabConfig->usesTLABs);

/* write minimum TLAB size */
_bufferWriter->writeLEB128(tlabConfig->minTLABSize);

/* write TLAB refill waste limit */
_bufferWriter->writeLEB128(tlabConfig->tlabRefillWasteLimit);

/* write event size */
writeEventSize(dataStart);
}

void
VM_JFRChunkWriter::writeInitialSystemPropertyEvents(J9JavaVM *vm)
{
Expand Down
9 changes: 9 additions & 0 deletions runtime/vm/JFRChunkWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ enum MetadataTypeID {
NativeLibraryID = 112,
ModuleRequireID = 113,
ModuleExportID = 114,
GCTLABConfigurationID = 132,
GCHeapConfigID = 133,
YoungGenerationConfigID = 134,
VirtualSpaceID = 149,
Expand Down Expand Up @@ -509,6 +510,8 @@ class VM_JFRChunkWriter {
writeGCHeapConfigurationEvent();

writeYoungGenerationConfigurationEvent();

writeGCTLABConfigurationEvent();
}

writePhysicalMemoryEvent();
Expand Down Expand Up @@ -545,6 +548,10 @@ class VM_JFRChunkWriter {
writeYoungGenerationConfigurationEvent();
}

if (_constantPoolTypes.shouldWriteGCTLABConfigurationEvent()) {
writeGCTLABConfigurationEvent();
}

if (_constantPoolTypes.shouldWritePhysicalMemory()) {
writePhysicalMemoryEvent();
}
Expand Down Expand Up @@ -953,6 +960,8 @@ class VM_JFRChunkWriter {

void writeYoungGenerationConfigurationEvent();

void writeGCTLABConfigurationEvent();

void writeInitialSystemPropertyEvents(J9JavaVM *vm);

void writeInitialEnvironmentVariableEvents();
Expand Down
38 changes: 38 additions & 0 deletions runtime/vm/JFRConstantPoolTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,12 @@ struct YoungGenerationConfigurationEntry {
U_64 newRatio;
};

struct GCTLABConfigurationEntry {
BOOLEAN usesTLABs;
U_64 minTLABSize;
U_64 tlabRefillWasteLimit;
};

struct VirtualizationInformationEntry {
const char *name;
};
Expand Down Expand Up @@ -474,6 +480,7 @@ struct JFRConstantEvents {
OSInformationEntry OSInfoEntry;
GCHeapConfigurationEntry GCHeapConfigEntry;
YoungGenerationConfigurationEntry YoungGenConfigEntry;
GCTLABConfigurationEntry GCTLABConfigEntry;
};

class VM_JFRConstantPoolTypes {
Expand Down Expand Up @@ -579,6 +586,7 @@ class VM_JFRConstantPoolTypes {
bool _shouldWriteModuleRequire;
bool _shouldWriteModuleExport;
bool _shouldWriteClassLoaderStatistics;
bool _shouldWriteGCTLABConfigurationEvent;

/* Processing buffers */
StackFrame *_currentStackFrameBuffer;
Expand Down Expand Up @@ -1314,6 +1322,11 @@ class VM_JFRConstantPoolTypes {
return _shouldWriteYoungGenerationConfigurationEvent;
}

bool shouldWriteGCTLABConfigurationEvent()
{
return _shouldWriteGCTLABConfigurationEvent;
}

bool shouldWritePhysicalMemory()
{
return _shouldWritePhysicalMemory;
Expand Down Expand Up @@ -1474,6 +1487,9 @@ class VM_JFRConstantPoolTypes {
case J9JFR_EVENT_TYPE_THREAD_ALLOCATION_STATISTICS:
addThreadAllocationStatistics((J9JFRThreadAllocationStatistics *)event);
break;
case J9JFR_EVENT_TYPE_GC_TLAB_CONFIGURATION:
_shouldWriteGCTLABConfigurationEvent = true;
break;
default:
Assert_VM_unreachable();
break;
Expand Down Expand Up @@ -1569,6 +1585,7 @@ class VM_JFRConstantPoolTypes {
initializeOSInformation(vm, result);
initializeGCHeapConfigurationEvent(vm);
initializeYoungGenerationConfigurationEvent(vm);
initializeGCTLABConfigurationEvent(vm);
}

/**
Expand Down Expand Up @@ -1828,6 +1845,26 @@ class VM_JFRConstantPoolTypes {
}
}

/**
* Initialize GCTLABConfigurationEntry
*
* @param vm[in] the J9JavaVM
*/
static void initializeGCTLABConfigurationEvent(J9JavaVM *vm)
{
J9MemoryManagerFunctions *mmFuncs = vm->memoryManagerFunctions;
GCTLABConfigurationEntry *tlabConfiguration = &(getJFRConstantEvents(vm)->GCTLABConfigEntry);

tlabConfiguration->usesTLABs = mmFuncs->j9gc_is_tlab_enabled(vm);
tlabConfiguration->minTLABSize = mmFuncs->j9gc_get_tlh_minimum_size(vm);
/* The closest OMR equivalent to tlabRefillWasteLimit is the
* initial value of abandonSize in TLHAllocationSupport::refresh(), which
* evaluates to max(tlhMinimumSize, tlhInitialSize / 2) on a fresh thread.

@amicic amicic Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a correct interpretation - the waste is roughly 1/2 of refresh size, because current refresh size typically dominates over initial/minimum

The hard part is that the refresh size is local to each allocating thread

For fast allocating threads refresh size is close to tlhMaximumSize, and for slow allocating it's close to tlhMinimumSize

If we want to generalize, we can say refresh is tlhMaximumSize across VM, in which case the waste limit is 1/2 of that. More correct would be to account for the largest refresh of all allocating threads (what would require to walk over all threads at the moment of the API invocation).

It may be relevant for X platforms where tlhMaximumSize defaults to 1MB, and is not always reached (by refresh). On other platforms were it defaults to 128KB it's pretty much always reached.

* We report tlhInitialSize / 2 as it dominates once refreshSize >= tlhInitialSize.
*/
tlabConfiguration->tlabRefillWasteLimit = mmFuncs->j9gc_get_tlh_initial_size(vm) / 2;
}

static uintptr_t recordSystemProcessEvent(uintptr_t pid, const char *commandLine, void *userData)
{
VM_JFRConstantPoolTypes *constantPoolTypes = reinterpret_cast<VM_JFRConstantPoolTypes *>(userData);
Expand Down Expand Up @@ -2246,6 +2283,7 @@ class VM_JFRConstantPoolTypes {
, _shouldWriteModuleRequire(false)
, _shouldWriteModuleExport(false)
, _shouldWriteClassLoaderStatistics(false)
, _shouldWriteGCTLABConfigurationEvent(false)
, _previousStackTraceEntry(NULL)
, _firstStackTraceEntry(NULL)
, _previousThreadEntry(NULL)
Expand Down
1 change: 1 addition & 0 deletions runtime/vm/JFRPeriodic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class JfrPeriodicEventSet {
static void requestThreadDump(J9VMThread *currentThread);
static void requestVirtualizationInformation(J9VMThread *currentThread);
static void requestYoungGenerationConfiguration(J9VMThread *currentThread);
static void requestGCTLABConfiguration(J9VMThread *currentThread);
};

#endif /* !defined(JFRPERIODIC_HPP_) */
13 changes: 13 additions & 0 deletions runtime/vm/jfr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ jfrEventSize(J9JFREvent *jfrEvent)
case J9JFR_EVENT_TYPE_MODULE_EXPORT:
case J9JFR_EVENT_TYPE_CLASS_LOADER_STATISTICS:
case J9JFR_EVENT_TYPE_NATIVE_LIBRARY:
case J9JFR_EVENT_TYPE_GC_TLAB_CONFIGURATION:
size = sizeof(J9JFREvent);
break;
default:
Expand Down Expand Up @@ -2234,6 +2235,9 @@ JfrPeriodicEventSet::requestEvent(J9VMThread *currentThread, jlong id)
case JfrYoungGenerationConfigurationEvent:
requestYoungGenerationConfiguration(currentThread);
break;
case JfrGCTLABConfigurationEvent:
requestGCTLABConfiguration(currentThread);
break;
default:
return JNI_FALSE;
}
Expand Down Expand Up @@ -2469,6 +2473,15 @@ jfrThreadAllocationStatisticsCallback(J9VMThread *currentThread, IDATA handlerKe
}

}

void
JfrPeriodicEventSet::requestGCTLABConfiguration(J9VMThread *currentThread)
{
J9JFREvent *jfrEvent = (J9JFREvent *)reserveBuffer(currentThread, currentThread, sizeof(J9JFREvent));
if (NULL != jfrEvent) {
initializeEventFields(currentThread, currentThread, jfrEvent, J9JFR_EVENT_TYPE_GC_TLAB_CONFIGURATION);
}
}
#endif /* JAVA_SPEC_VERSION >= 17 */

void
Expand Down
9 changes: 9 additions & 0 deletions test/functional/cmdLineTests/jfr/jfrevents.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex
<output type="success" caseSensitive="yes" regex="no">jdk.YoungGenerationConfiguration</output>
<output type="failure" caseSensitive="yes" regex="no">jfr print: could not read recording</output>
</test>
<test id="test jfr GC TLAB configuration - approx 30seconds">
<command>$JFR_EXE$ print --xml --events "GCTLABConfiguration" defaultJ9recording.jfr</command>
<output type="required" caseSensitive="yes" regex="no">http://www.w3.org/2001/XMLSchema-instance</output>
<output type="success" caseSensitive="yes" regex="no">jdk.GCTLABConfiguration</output>
<output type="required" caseSensitive="yes" regex="no">usesTLABs</output>
<output type="required" caseSensitive="yes" regex="no">minTLABSize</output>
<output type="required" caseSensitive="yes" regex="no">tlabRefillWasteLimit</output>
<output type="failure" caseSensitive="yes" regex="no">jfr print: could not read recording</output>
</test>
<test id="test jfr system process - approx 30 seconds">
<command>$JFR_EXE$ print --xml --events "SystemProcess" defaultJ9recording.jfr</command>
<output type="required" caseSensitive="yes" regex="no">http://www.w3.org/2001/XMLSchema-instance</output>
Expand Down