Skip to content

Commit

Permalink
(0.48) Cache rom to ram mappings for exception backtrace
Browse files Browse the repository at this point in the history
Create a hash table and access mutex to cache romClass to ramClass mappings from
exceptiondescribe.c:findJ9ClassForROMClass. This saves time iterating over multiple
class loaders to find a ram class. Entries will be deleted when a class
is unloaded or redefined.

Signed-off-by: Theresa Mammarella <Theresa.T.Mammarella@ibm.com>
  • Loading branch information
theresa-m committed Sep 19, 2024
1 parent ff860c5 commit 00eea85
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 9 deletions.
14 changes: 14 additions & 0 deletions runtime/nls/shrc/j9shr.nls
Original file line number Diff line number Diff line change
Expand Up @@ -6943,3 +6943,17 @@ J9NLS_SHRC_CM_PRINTSTATS_PAGESIZEE.explanation=NOTAG
J9NLS_SHRC_CM_PRINTSTATS_PAGESIZEE.system_action=
J9NLS_SHRC_CM_PRINTSTATS_PAGESIZEE.user_response=
# END NON-TRANSLATABLE

J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMHASHTABLE=Cannot allocate ROM to RAM class hash table in shrinit
# START NON-TRANSLATABLE
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMHASHTABLE.explanation=The system is unable to obtain sufficient native memory to allocate internal data structures.
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMHASHTABLE.system_action=The JVM terminates, unless you have specified the nonfatal option with "-Xshareclasses:nonfatal", in which case the JVM attempts to start up without using Shared Classes.
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMHASHTABLE.user_response=System is running low on memory resource for the JVM to start up properly. Check system memory configuration. If the situation persists, contact your service representative.
# END NON-TRANSLATABLE

J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMMUTEX=Cannot allocate ROM to RAM class mutex in shrinit
# START NON-TRANSLATABLE
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMMUTEX.explanation=The system is unable to obtain sufficient native memory to allocate internal data structures.
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMMUTEX.system_action=The JVM terminates, unless you have specified the nonfatal option with "-Xshareclasses:nonfatal", in which case the JVM attempts to start up without using Shared Classes.
J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMMUTEX.user_response=System is running low on memory resource for the JVM to start up properly. Check system memory configuration. If the situation persists, contact your service representative.
# END NON-TRANSLATABLE
5 changes: 5 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,11 @@ typedef struct J9SharedClassConfig {
const char* cacheName;
I_8 layer;
U_64 readOnlyCacheRuntimeFlags;
/* This table is a cache for exceptiondescribe.c:findJ9ClassForROMClass
* and does not contain mappings for every romClass to ramClass.
*/
struct J9HashTable *romToRamHashTable;
omrthread_rwmutex_t romToRamHashTableMutex;
} J9SharedClassConfig;

typedef struct J9SharedClassPreinitConfig {
Expand Down
12 changes: 12 additions & 0 deletions runtime/shared_common/include/SCQueryFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ j9shr_Query_PopulatePreinitConfigDefaults(J9JavaVM *vm, J9SharedClassPreinitConf
}
#endif

typedef struct RomToRamEntry {
J9Class *ramClass;
} RomToRamEntry;

typedef struct RomToRamQueryEntry {
/* J9ROMClass address | ROM_TO_RAM_QUERY_TAG */
J9ROMClass *romClass;
} RomToRamQueryEntry;

/* Used to tell RomToRamQueryEntry apart from RomToRamEntry. */
#define ROM_TO_RAM_QUERY_TAG 0x1

#ifdef __cplusplus
}/*extern "C"*/
#endif
Expand Down
98 changes: 93 additions & 5 deletions runtime/shared_common/shrinit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ extern "C" {
#include "simplepool_api.h"
#include "SCStringInternHelpers.h"
#include <string.h>
#include "SCQueryFunctions.h"
#include "util_api.h"
}

Expand Down Expand Up @@ -412,6 +413,12 @@ static char* generateStartupHintsKey(J9JavaVM *vm);
static void fetchStartupHintsFromSharedCache(J9VMThread* vmThread);
static void findExistingCacheLayerNumbers(J9JavaVM* vm, const char* ctrlDirName, const char* cacheName, U_64 runtimeFlags, I_8 *maxLayerNo);
static IDATA sysinfoGetUserNameHelper(J9JavaVM *vm, UDATA verboseFlags, char *buffer, UDATA length);
static UDATA romToRamGetRomAddress(void *item);
static UDATA romToRamHashFn(void *item, void *userData);
static UDATA romToRamEqualFn(void *left, void *right, void *userData);
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
static void romToRamRemoveEntry(J9HookInterface **hookInterface, UDATA eventNum, void *voidData, void *userData);
#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */

typedef struct J9SharedVerifyStringTable {
void *romClassAreaStart;
Expand Down Expand Up @@ -3183,6 +3190,49 @@ j9shr_isBCIEnabled(J9JavaVM *vm)
return (0 != (vm->sharedClassConfig->runtimeFlags & J9SHR_RUNTIMEFLAG_ENABLE_BCI));
}

static UDATA
romToRamGetRomAddress(void *item)
{
RomToRamQueryEntry *queryEntry = (RomToRamQueryEntry *)item;
UDATA romAddress = (UDATA)(queryEntry->romClass);
if (J9_ARE_ALL_BITS_SET(romAddress, ROM_TO_RAM_QUERY_TAG)) {
return romAddress & ~ROM_TO_RAM_QUERY_TAG;
} else {
RomToRamEntry *entry = (RomToRamEntry *)item;
return (UDATA)(entry->ramClass->romClass);
}
}

/* THREADING: Must be protected by J9SharedClassConfig.romToRamHashTableMutex */
static UDATA
romToRamHashFn(void *item, void *userData)
{
return romToRamGetRomAddress(item);
}

/* THREADING: Must be protected by J9SharedClassConfig.romToRamHashTableMutex */
static UDATA
romToRamEqualFn(void *left, void *right, void *userData)
{
return (romToRamGetRomAddress(left) == romToRamGetRomAddress(right));
}

#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
static void
romToRamRemoveEntry(J9HookInterface **hookInterface, UDATA eventNum, void *voidData, void *userData)
{
J9VMClassesUnloadEvent *data = (J9VMClassesUnloadEvent *)voidData;
J9SharedClassConfig *config = data->currentThread->javaVM->sharedClassConfig;
omrthread_rwmutex_enter_write(config->romToRamHashTableMutex);
for (J9Class *ramClass = data->classesToUnload; ramClass; ramClass = ramClass->gcLink) {
RomToRamEntry entry;
entry.ramClass = ramClass;
hashTableRemove(config->romToRamHashTable, &entry);
}
omrthread_rwmutex_exit_write(config->romToRamHashTableMutex);
}
#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */

/**
* JVM Initialisation processing for shared classes
*
Expand All @@ -3207,7 +3257,7 @@ j9shr_init(J9JavaVM *vm, UDATA loadFlags, UDATA* nonfatal)
U_64 runtimeFlags = vm->sharedCacheAPI->runtimeFlags;
UDATA verboseFlags = vm->sharedCacheAPI->verboseFlags;
UDATA printStatsOptions = vm->sharedCacheAPI->printStatsOptions;
J9HookInterface** hook = vm->internalVMFunctions->getVMHookInterface(vm);
J9HookInterface** vmHooks = vm->internalVMFunctions->getVMHookInterface(vm);
UDATA parseResult = vm->sharedCacheAPI->parseResult;
IDATA rc, rcStartup = 0;
const char* cacheName = vm->sharedCacheAPI->cacheName;
Expand Down Expand Up @@ -3515,6 +3565,24 @@ j9shr_init(J9JavaVM *vm, UDATA loadFlags, UDATA* nonfatal)
config->jclURLCache = NULL;
config->jclURLHashTable = NULL;
config->jclUTF8HashTable = NULL;
if (J9_ARE_ALL_BITS_SET(runtimeFlags, J9SHR_RUNTIMEFLAG_ENABLE_CACHE_NON_BOOT_CLASSES)) {
if (omrthread_rwmutex_init(&(config->romToRamHashTableMutex), 0, "romToRamHashTable mutex")) {
SHRINIT_ERR_TRACE(verboseFlags, J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMMUTEX);
goto _error;
}
omrthread_rwmutex_enter_write(config->romToRamHashTableMutex);
config->romToRamHashTable = hashTableNew(OMRPORT_FROM_J9PORT(vm->portLibrary),
J9_GET_CALLSITE(), 0, sizeof(RomToRamEntry), sizeof(char *), 0,
J9MEM_CATEGORY_CLASSES, romToRamHashFn, romToRamEqualFn, NULL, NULL);
if (NULL == config->romToRamHashTable) {
omrthread_rwmutex_exit_write(config->romToRamHashTableMutex);
omrthread_rwmutex_destroy(config->romToRamHashTableMutex);
SHRINIT_ERR_TRACE(verboseFlags, J9NLS_SHRC_SHRINIT_FAILURE_CREATE_ROMTORAMHASHTABLE);
goto _error;
}
omrthread_rwmutex_exit_write(config->romToRamHashTableMutex);
}

config->jclJ9ClassPathEntryPool = pool_new(sizeof(struct J9ClassPathEntry), 0, 0, 0, J9_GET_CALLSITE(), J9MEM_CATEGORY_CLASSES, POOL_FOR_PORT(vm->portLibrary));
if (!(config->jclJ9ClassPathEntryPool)) {
SHRINIT_ERR_TRACE(verboseFlags, J9NLS_SHRC_SHRINIT_FAILURE_CREATE_POOL);
Expand Down Expand Up @@ -3556,7 +3624,7 @@ j9shr_init(J9JavaVM *vm, UDATA loadFlags, UDATA* nonfatal)
}

/* Register hooks */
(*hook)->J9HookRegisterWithCallSite(hook, J9HOOK_VM_FIND_LOCALLY_DEFINED_CLASS, hookFindSharedClass, OMR_GET_CALLSITE(), NULL);
(*vmHooks)->J9HookRegisterWithCallSite(vmHooks, J9HOOK_VM_FIND_LOCALLY_DEFINED_CLASS, hookFindSharedClass, OMR_GET_CALLSITE(), NULL);

/* We don't need the string table when running with -Xshareclasses:print<XXXX>Stats.
* This is because the JVM is terminated, after displaying the requested information.
Expand Down Expand Up @@ -3710,10 +3778,13 @@ j9shr_init(J9JavaVM *vm, UDATA loadFlags, UDATA* nonfatal)
}

if (0 != (runtimeFlags & J9SHR_RUNTIMEFLAG_ADD_TEST_JITHINT)) {
J9HookInterface** hook = vm->internalVMFunctions->getVMHookInterface(vm);
(*hook)->J9HookRegisterWithCallSite(hook, J9HOOK_VM_FIND_LOCALLY_DEFINED_CLASS, addTestJitHint, OMR_GET_CALLSITE(), NULL);
(*vmHooks)->J9HookRegisterWithCallSite(vmHooks, J9HOOK_VM_FIND_LOCALLY_DEFINED_CLASS, addTestJitHint, OMR_GET_CALLSITE(), NULL);
}

#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
if (NULL != vm->sharedClassConfig->romToRamHashTable) {
(*vmHooks)->J9HookRegisterWithCallSite(vmHooks, J9HOOK_VM_CLASSES_UNLOAD, romToRamRemoveEntry, OMR_GET_CALLSITE(), NULL);
}
#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */
if (doPrintStats) {
if (j9shr_print_stats(vm, parseResult, runtimeFlags, printStatsOptions) != -1) {
*nonfatal = 0; /* Nonfatal should be ignored for stats utilities */
Expand Down Expand Up @@ -4137,6 +4208,11 @@ j9shr_guaranteed_exit(J9JavaVM *vm, BOOLEAN exitForDebug)
*/
J9HookInterface** hook = vm->internalVMFunctions->getVMHookInterface(vm);
(*hook)->J9HookUnregister(hook, J9HOOK_VM_FIND_LOCALLY_DEFINED_CLASS, hookFindSharedClass, NULL);
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
if (NULL != vm->sharedClassConfig->romToRamHashTable) {
(*hook)->J9HookUnregister(hook, J9HOOK_VM_CLASSES_UNLOAD, romToRamRemoveEntry, NULL);
}
#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */
J9HookInterface **shcHooks;
shcHooks = zip_getVMZipCachePoolHookInterface((J9ZipCachePool *)vm->zipCachePool);
(*shcHooks)->J9HookUnregister(shcHooks, J9HOOK_VM_ZIP_LOAD, j9shr_hookZipLoadEvent, NULL);
Expand Down Expand Up @@ -4207,6 +4283,8 @@ j9shr_shutdown(J9JavaVM *vm)
J9SharedStringFarm* jclStringFarm = config->jclStringFarm;
J9HashTable* urlHashTable = config->jclURLHashTable;
J9HashTable* utfHashTable = config->jclUTF8HashTable;
J9HashTable *romToRamHashTable = config->romToRamHashTable;
omrthread_rwmutex_t romToRamHashTableMutex = config->romToRamHashTableMutex;
J9VMThread* currentThread = vm->internalVMFunctions->currentVMThread(vm);

/* Free all of the cached ClasspathItems */
Expand Down Expand Up @@ -4251,6 +4329,16 @@ j9shr_shutdown(J9JavaVM *vm)
if (utfHashTable) {
hashTableFree(utfHashTable);
}
if (NULL != romToRamHashTable) {
omrthread_rwmutex_enter_write(romToRamHashTableMutex);
config->romToRamHashTable = NULL;
config->romToRamHashTableMutex = NULL;
if (romToRamHashTable) {
hashTableFree(romToRamHashTable);
}
omrthread_rwmutex_exit_write(romToRamHashTableMutex);
omrthread_rwmutex_destroy(romToRamHashTableMutex);
}

/* Kill the string farm */

Expand Down
8 changes: 8 additions & 0 deletions runtime/util/hshelp.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "j2sever.h"
#include "vrfytbl.h"
#include "bytecodewalk.h"
#include "../shared_common/include/SCQueryFunctions.h"

/* Static J9ITable used as a non-NULL iTable cache value by classes that don't implement any interfaces */
const J9ITable invalidITable = { (J9Class *) (UDATA) 0xDEADBEEF, 0, (J9ITable *) NULL };
Expand Down Expand Up @@ -2641,6 +2642,13 @@ recreateRAMClasses(J9VMThread * currentThread, J9HashTable * classHashTable, J9H
/* Delete original class from defining loader's class table */
if (!fastHCR) {
vmFuncs->hashClassTableDelete(classLoader, J9UTF8_DATA(className), J9UTF8_LENGTH(className));
if ((NULL != vm->sharedClassConfig) && (NULL != vm->sharedClassConfig->romToRamHashTable)) {
omrthread_rwmutex_enter_write(vm->sharedClassConfig->romToRamHashTableMutex);
RomToRamEntry entry;
entry.ramClass = originalRAMClass;
hashTableRemove(vm->sharedClassConfig->romToRamHashTable, &entry);
omrthread_rwmutex_exit_write(vm->sharedClassConfig->romToRamHashTableMutex);
}
}

/* Create new RAM class */
Expand Down
37 changes: 33 additions & 4 deletions runtime/vm/exceptiondescribe.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,33 @@ findJ9ClassForROMClass(J9VMThread *vmThread, J9ROMClass *romClass, J9ClassLoader
{
J9UTF8 const *utfClassName = J9ROMCLASS_CLASSNAME(romClass);
J9JavaVM *vm = vmThread->javaVM;
J9SharedClassConfig *config = vm->sharedClassConfig;
J9Class* ret = NULL;
if (j9shr_Query_IsAddressInCache(vm, romClass, romClass->romSize)) {

if (_J9ROMCLASS_J9MODIFIER_IS_SET(romClass, J9AccClassAnonClass)) {
/* Anonymous classes are not allowed in any class loader hash table. */
return NULL;
}

if (j9shr_Query_IsAddressInCache(vm, romClass, romClass->romSize)
&& (NULL != config->romToRamHashTable)
) {
J9ClassLoaderWalkState walkState;
J9ClassLoader* classLoader = NULL;
BOOLEAN fastMode = J9_ARE_ALL_BITS_SET(vm->extendedRuntimeFlags, J9_EXTENDED_RUNTIME_FAST_CLASS_HASH_TABLE);
J9Class* ramClass = NULL;
RomToRamEntry *resultEntry;
RomToRamQueryEntry searchEntry;
searchEntry.romClass = (J9ROMClass *)((UDATA)romClass | ROM_TO_RAM_QUERY_TAG);

omrthread_rwmutex_enter_read(config->romToRamHashTableMutex);
resultEntry = hashTableFind(config->romToRamHashTable, &searchEntry);
omrthread_rwmutex_exit_read(config->romToRamHashTableMutex);
if (NULL != resultEntry) {
ret = resultEntry->ramClass;
goto done;
}

if (!fastMode) {
omrthread_monitor_enter(vm->classTableMutex);
}
Expand All @@ -259,7 +280,7 @@ findJ9ClassForROMClass(J9VMThread *vmThread, J9ROMClass *romClass, J9ClassLoader
if (!fastMode) {
omrthread_monitor_exit(vm->classTableMutex);
}
goto done;
goto cacheresult;
}

ramClass = hashClassTableAt(vm->extensionClassLoader, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName));
Expand All @@ -271,7 +292,7 @@ findJ9ClassForROMClass(J9VMThread *vmThread, J9ROMClass *romClass, J9ClassLoader
if (!fastMode) {
omrthread_monitor_exit(vm->classTableMutex);
}
goto done;
goto cacheresult;
}

ramClass = hashClassTableAt(vm->applicationClassLoader, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName));
Expand All @@ -283,7 +304,7 @@ findJ9ClassForROMClass(J9VMThread *vmThread, J9ROMClass *romClass, J9ClassLoader
if (!fastMode) {
omrthread_monitor_exit(vm->classTableMutex);
}
goto done;
goto cacheresult;
}

classLoader = vm->internalVMFunctions->allClassLoadersStartDo(&walkState, vm, 0);
Expand All @@ -307,6 +328,14 @@ findJ9ClassForROMClass(J9VMThread *vmThread, J9ROMClass *romClass, J9ClassLoader
if (!fastMode) {
omrthread_monitor_exit(vm->classTableMutex);
}
cacheresult:
if (NULL != ret) {
RomToRamEntry newEntry;
newEntry.ramClass = ret;
omrthread_rwmutex_enter_write(config->romToRamHashTableMutex);
hashTableAdd(config->romToRamHashTable, &newEntry);
omrthread_rwmutex_exit_write(config->romToRamHashTableMutex);
}
} else {
ret = peekClassHashTable(vmThread, *resultClassLoader, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName));
}
Expand Down

0 comments on commit 00eea85

Please sign in to comment.