Path: blob/master/runtime/compiler/env/ClassLoaderTable.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2000, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "AtomicSupport.hpp"23#include "control/CompilationThread.hpp"24#include "env/ClassLoaderTable.hpp"25#include "env/J9SharedCache.hpp"26#include "env/VerboseLog.hpp"27#include "infra/MonitorTable.hpp"282930enum TableKind { Loader, Chain, Name };3132// To make the three-way map between class loaders, class chains, and class names more efficient, this33// struct is linked into three linked lists - one for each hash table in TR_PersistentClassLoaderTable.34struct TR_ClassLoaderInfo35{36TR_PERSISTENT_ALLOC(TR_Memory::PersistentCHTable)3738TR_ClassLoaderInfo(void *loader, void *chain) :39_loader(loader), _loaderTableNext(NULL),40_chain(chain), _chainTableNext(NULL)41#if defined(J9VM_OPT_JITSERVER)42, _nameTableNext(NULL)43#endif /* defined(J9VM_OPT_JITSERVER) */44{ }4546const J9UTF8 *name(TR_J9SharedCache *sharedCache) const47{48return J9ROMCLASS_CLASSNAME(sharedCache->startingROMClassOfClassChain((uintptr_t *)_chain));49}5051template<TableKind T> TR_ClassLoaderInfo *&next();52template<TableKind T> bool equals(const void *key) const;5354void *const _loader;55TR_ClassLoaderInfo *_loaderTableNext;56void *const _chain;57TR_ClassLoaderInfo *_chainTableNext;58#if defined(J9VM_OPT_JITSERVER)59TR_ClassLoaderInfo *_nameTableNext;60#endif /* defined(J9VM_OPT_JITSERVER) */61};626364template<TableKind T> static size_t hash(const void *key);6566template<TableKind T> static TR_ClassLoaderInfo *67lookup(TR_ClassLoaderInfo *const *table, size_t &index, TR_ClassLoaderInfo *&prev, const void *key)68{69index = hash<T>(key) % CLASSLOADERTABLE_SIZE;70TR_ClassLoaderInfo *info = table[index];71prev = NULL;72while (info && !info->equals<T>(key))73{74prev = info;75info = info->next<T>();76}77return info;78}7980template<TableKind T> static void81insert(TR_ClassLoaderInfo *info, TR_ClassLoaderInfo **table, size_t index)82{83info->next<T>() = table[index];84// Write barrier guarantees that a reader thread traversing the list will read85// the new list head only after its next field is already set to the old head.86VM_AtomicSupport::writeBarrier();87table[index] = info;88}8990template<TableKind T> static void91remove(TR_ClassLoaderInfo *info, TR_ClassLoaderInfo *prev, TR_ClassLoaderInfo **table, size_t index)92{93if (prev)94prev->next<T>() = info->next<T>();95else96table[index] = info->next<T>();97}9899100template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Loader>() { return _loaderTableNext; }101template<> bool TR_ClassLoaderInfo::equals<Loader>(const void *loader) const { return loader == _loader; }102// Remove trailing zero bits in aligned pointer for better hash distribution103template<> size_t hash<Loader>(const void *loader) { return (uintptr_t)loader >> 3; }104105template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Chain>() { return _chainTableNext; }106template<> bool TR_ClassLoaderInfo::equals<Chain>(const void *chain) const { return chain == _chain; }107// Remove trailing zero bits in aligned pointer for better hash distribution108template<> size_t hash<Chain>(const void *chain) { return (uintptr_t)chain >> 3; }109110111#if defined(J9VM_OPT_JITSERVER)112113template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Name>() { return _nameTableNext; }114115struct NameKey116{117const uint8_t *_data;118size_t _length;119TR_J9SharedCache *_sharedCache;120};121122template<> bool123TR_ClassLoaderInfo::equals<Name>(const void *keyPtr) const124{125auto key = (const NameKey *)keyPtr;126const J9UTF8 *str = name(key->_sharedCache);127return J9UTF8_DATA_EQUALS(J9UTF8_DATA(str), J9UTF8_LENGTH(str), key->_data, key->_length);128}129130template<> size_t131hash<Name>(const void *keyPtr)132{133auto key = (const NameKey *)keyPtr;134size_t h = 0;135for (size_t i = 0; i < key->_length; ++i)136h = (h << 5) - h + key->_data[i];137return h;138}139140#endif /* defined(J9VM_OPT_JITSERVER) */141142143TR_PersistentClassLoaderTable::TR_PersistentClassLoaderTable(TR_PersistentMemory *persistentMemory) :144_persistentMemory(persistentMemory), _sharedCache(NULL)145{146memset(_loaderTable, 0, sizeof(_loaderTable));147memset(_chainTable, 0, sizeof(_chainTable));148#if defined(J9VM_OPT_JITSERVER)149memset(_nameTable, 0, sizeof(_nameTable));150#endif /* defined(J9VM_OPT_JITSERVER) */151}152153154//NOTE: Class loader table doesn't require any additional locking for synchronization.155// Writers are always mutually exclusive with each other. Readers cannot be concurrent156// with the writers that remove entries from the table. Traversing linked lists in hash157// buckets can be concurrent with inserting new entries (which only needs a write barrier).158159static bool160hasSharedVMAccess(J9VMThread *vmThread)161{162return (vmThread->publicFlags & J9_PUBLIC_FLAGS_VM_ACCESS) && !vmThread->omrVMThread->exclusiveCount;163}164165166void167TR_PersistentClassLoaderTable::associateClassLoaderWithClass(J9VMThread *vmThread, void *loader,168TR_OpaqueClassBlock *clazz)169{170// Since current thread has shared VM access and holds the classTableMutex,171// no other thread can be modifying the table at the same time.172TR_ASSERT(hasSharedVMAccess(vmThread), "Must have shared VM access");173TR_ASSERT(TR::MonitorTable::get()->getClassTableMutex()->owned_by_self(), "Must hold classTableMutex");174if (!_sharedCache)175return;176177// Lookup by class loader and check if it already has an associated class178size_t index;179TR_ClassLoaderInfo *prev;180TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);181if (info)182return;183184#if defined(J9VM_OPT_JITSERVER)185bool useAOTCache = _persistentMemory->getPersistentInfo()->getJITServerUseAOTCache();186const J9UTF8 *nameStr = J9ROMCLASS_CLASSNAME(((J9Class *)clazz)->romClass);187const uint8_t *name = J9UTF8_DATA(nameStr);188uint16_t nameLength = J9UTF8_LENGTH(nameStr);189#endif /* defined(J9VM_OPT_JITSERVER) */190191void *chain = _sharedCache->rememberClass(clazz);192if (!chain)193{194#if defined(J9VM_OPT_JITSERVER)195if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))196TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,197"ERROR: Failed to get class chain for %.*s loaded by %p",198nameLength, (const char *)name, loader199);200#endif /* defined(J9VM_OPT_JITSERVER) */201return;202}203TR_ASSERT(_sharedCache->isPointerInSharedCache(chain), "Class chain must be in SCC");204205info = new (_persistentMemory) TR_ClassLoaderInfo(loader, chain);206if (!info)207{208// This is a bad situation because we can't associate the right class with this class loader.209// Probably not critical if multiple class loaders aren't routinely loading the exact same class.210//TODO: Prevent this class loader from associating with a different class211#if defined(J9VM_OPT_JITSERVER)212if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))213TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,214"ERROR: Failed to associate class %.*s chain %p with loader %p",215nameLength, (const char *)name, chain, loader216);217#endif /* defined(J9VM_OPT_JITSERVER) */218return;219}220insert<Loader>(info, _loaderTable, index);221222// Lookup by class chain and check if was already associated with another class loader223TR_ClassLoaderInfo *otherInfo = lookup<Chain>(_chainTable, index, prev, chain);224if (otherInfo)225{226// There is more than one class loader with identical first loaded class.227// Current heuristic doesn't work in this scenario, but it doesn't break228// correctness, and in the worst case can only result in failed AOT loads.229// We have added this loader to _classLoaderTable, which has a nice side230// benefit that we won't keep trying to add it, so leave it there.231#if defined(J9VM_OPT_JITSERVER)232if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))233TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,234"ERROR: Class %.*s chain %p already associated with loader %p != %p",235nameLength, (const char *)name, chain, otherInfo->_loader, loader236);237#endif /* defined(J9VM_OPT_JITSERVER) */238return;239}240insert<Chain>(info, _chainTable, index);241242#if defined(J9VM_OPT_JITSERVER)243if (useAOTCache)244{245// Lookup by class name and check if it was already associated with another class loader246NameKey key { name, nameLength, _sharedCache };247otherInfo = lookup<Name>(_nameTable, index, prev, &key);248if (otherInfo)249{250// There is more than one class loader with the same name of the first loaded251// class (but the classes themselves are not identical). Current AOT cache252// heuristic doesn't work in this scenario, but it doesn't break correctness,253// and in the worst case can only result in failed AOT method deserialization.254if (TR::Options::getVerboseOption(TR_VerboseJITServer))255TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,256"ERROR: Class name %.*s already associated with loader %p != %p",257nameLength, (const char *)name, otherInfo->_loader, loader258);259return;260}261insert<Name>(info, _nameTable, index);262263if (TR::Options::getVerboseOption(TR_VerboseJITServer))264TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,265"Associated class loader %p with class %.*s chain %p",266loader, nameLength, (const char *)name, chain267);268}269#endif /* defined(J9VM_OPT_JITSERVER) */270}271272273static void274assertCurrentThreadCanRead()275{276// To guarantee that reading the table is not concurrent with class loader removal during GC,277// current thread must either have shared VM access or hold the classUnloadMonitor for reading.278#if defined(DEBUG) || defined(PROD_WITH_ASSUMES)279TR::Compilation *comp = TR::comp();280TR_ASSERT(hasSharedVMAccess(comp->j9VMThread()) ||281(TR::MonitorTable::get()->getClassUnloadMonitorHoldCount(comp->getCompThreadID()) > 0),282"Must either have shared VM access of hold classUnloadMonitor for reading");283#endif /* defined(DEBUG) || defined(PROD_WITH_ASSUMES) */284}285286void *287TR_PersistentClassLoaderTable::lookupClassChainAssociatedWithClassLoader(void *loader) const288{289assertCurrentThreadCanRead();290if (!_sharedCache)291return NULL;292293size_t index;294TR_ClassLoaderInfo *prev;295TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);296return info ? info->_chain : NULL;297}298299void *300TR_PersistentClassLoaderTable::lookupClassLoaderAssociatedWithClassChain(void *chain) const301{302assertCurrentThreadCanRead();303if (!_sharedCache)304return NULL;305306size_t index;307TR_ClassLoaderInfo *prev;308TR_ClassLoaderInfo *info = lookup<Chain>(_chainTable, index, prev, chain);309return info ? info->_loader : NULL;310}311312#if defined(J9VM_OPT_JITSERVER)313314std::pair<void *, void *>315TR_PersistentClassLoaderTable::lookupClassLoaderAndChainAssociatedWithClassName(const uint8_t *data, size_t length) const316{317assertCurrentThreadCanRead();318if (!_sharedCache)319return { NULL, NULL };320321NameKey key { data, length, _sharedCache };322size_t index;323TR_ClassLoaderInfo *prev;324TR_ClassLoaderInfo *info = lookup<Name>(_nameTable, index, prev, &key);325if (!info)326return { NULL, NULL };327return { info->_loader, info->_chain };328}329330#endif /* defined(J9VM_OPT_JITSERVER) */331332333void334TR_PersistentClassLoaderTable::removeClassLoader(J9VMThread *vmThread, void *loader)335{336// Since current thread has exclusive VM access and holds the classUnloadMonitor337// for writing (NOTE: we don't have an assertion for that due to lack of API),338// no other thread can be modifying the table at the same time.339TR_ASSERT((vmThread->publicFlags & J9_PUBLIC_FLAGS_VM_ACCESS) && vmThread->omrVMThread->exclusiveCount,340"Must have exclusive VM access");341if (!_sharedCache)342return;343344// Remove from the table indexed by class loader345size_t index;346TR_ClassLoaderInfo *prev;347TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);348if (!info)349return;350remove<Loader>(info, prev, _loaderTable, index);351352// Remove from the table indexed by class chain353TR_ClassLoaderInfo *otherInfo = lookup<Chain>(_chainTable, index, prev, info->_chain);354if (otherInfo == info)// Otherwise the entry belongs to another class loader355remove<Chain>(info, prev, _chainTable, index);356357#if defined(J9VM_OPT_JITSERVER)358if (_persistentMemory->getPersistentInfo()->getJITServerUseAOTCache())359{360// Remove from the table indexed by class name361const J9UTF8 *nameStr = info->name(_sharedCache);362const uint8_t *name = J9UTF8_DATA(nameStr);363uint16_t nameLength = J9UTF8_LENGTH(nameStr);364365NameKey key { name, nameLength, _sharedCache };366otherInfo = lookup<Name>(_nameTable, index, prev, &key);367if (otherInfo == info)// Otherwise the entry belongs to another class loader368remove<Name>(info, prev, _nameTable, index);369370if (TR::Options::getVerboseOption(TR_VerboseJITServer))371TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,372"Removed class loader %p associated with class %.*s chain %p",373loader, nameLength, (const char *)name, info->_chain374);375}376#endif /* defined(J9VM_OPT_JITSERVER) */377378_persistentMemory->freePersistentMemory(info);379}380381382