From 00eea85dce4de1a8ce019c15a9a41c779163be70 Mon Sep 17 00:00:00 2001 From: Theresa Mammarella Date: Mon, 16 Sep 2024 14:56:24 -0400 Subject: [PATCH] (0.48) Cache rom to ram mappings for exception backtrace 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 --- runtime/nls/shrc/j9shr.nls | 14 +++ runtime/oti/j9nonbuilder.h | 5 + .../shared_common/include/SCQueryFunctions.h | 12 +++ runtime/shared_common/shrinit.cpp | 98 ++++++++++++++++++- runtime/util/hshelp.c | 8 ++ runtime/vm/exceptiondescribe.c | 37 ++++++- 6 files changed, 165 insertions(+), 9 deletions(-) diff --git a/runtime/nls/shrc/j9shr.nls b/runtime/nls/shrc/j9shr.nls index eaf6237162b..1a9184aedc6 100644 --- a/runtime/nls/shrc/j9shr.nls +++ b/runtime/nls/shrc/j9shr.nls @@ -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 diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index c6669ec4484..532dec3e306 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -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 { diff --git a/runtime/shared_common/include/SCQueryFunctions.h b/runtime/shared_common/include/SCQueryFunctions.h index cc7e5048498..377d7aa4df3 100644 --- a/runtime/shared_common/include/SCQueryFunctions.h +++ b/runtime/shared_common/include/SCQueryFunctions.h @@ -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 diff --git a/runtime/shared_common/shrinit.cpp b/runtime/shared_common/shrinit.cpp index a7ce17c9a8b..fcc5a40ef5b 100644 --- a/runtime/shared_common/shrinit.cpp +++ b/runtime/shared_common/shrinit.cpp @@ -68,6 +68,7 @@ extern "C" { #include "simplepool_api.h" #include "SCStringInternHelpers.h" #include +#include "SCQueryFunctions.h" #include "util_api.h" } @@ -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; @@ -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 * @@ -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; @@ -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); @@ -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:printStats. * This is because the JVM is terminated, after displaying the requested information. @@ -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 */ @@ -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); @@ -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 */ @@ -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 */ diff --git a/runtime/util/hshelp.c b/runtime/util/hshelp.c index d20d203077e..ced62078784 100644 --- a/runtime/util/hshelp.c +++ b/runtime/util/hshelp.c @@ -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 }; @@ -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 */ diff --git a/runtime/vm/exceptiondescribe.c b/runtime/vm/exceptiondescribe.c index 9e259c49ca3..3b67db189e1 100644 --- a/runtime/vm/exceptiondescribe.c +++ b/runtime/vm/exceptiondescribe.c @@ -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); } @@ -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)); @@ -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)); @@ -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); @@ -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)); }