Skip to content

Commit

Permalink
wip Stable and unique Lambda and LambdaForm class names
Browse files Browse the repository at this point in the history
  • Loading branch information
ThanHenderson committed Aug 6, 2024
1 parent d776a4a commit 2b46b6c
Show file tree
Hide file tree
Showing 19 changed files with 213 additions and 261 deletions.
21 changes: 1 addition & 20 deletions runtime/bcutil/ComparingCursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,29 +428,10 @@ ComparingCursor::shouldCheckForEquality(DataType dataType, U_32 u32Value)
}

switch (dataType) {
case SRP_TO_UTF8_CLASS_NAME:
#if JAVA_SPEC_VERSION < 21
if (_context->isLambdaClass() && _romClassIsShared) {
/*
* If the class is a lambda class, don't compare the class names because lambda
* classes might have different index numbers from run to run.
*/
return false;
}
#endif /* JAVA_SPEC_VERSION < 21 */
break;
case SRP_TO_UTF8_CLASS_NAME: /* fall through */
case BYTECODE: /* fall through */
case GENERIC: /* fall through */
case CLASS_FILE_SIZE: /* fall through */
#if JAVA_SPEC_VERSION < 21
if ((CLASS_FILE_SIZE == dataType)
&& _context->isLambdaClass()
&& _romClassIsShared
) {
/* If comparing a lambda class from the shared cache, class file size comparison is already done in ROMClassBuilder::compareROMClassForEquality(). */
return false;
}
#endif /* JAVA_SPEC_VERSION < 21 */
case SRP_TO_DEBUG_DATA: /* fall through */
case SRP_TO_GENERIC: /* fall through */
case SRP_TO_UTF8: /* fall through */
Expand Down
98 changes: 9 additions & 89 deletions runtime/bcutil/ROMClassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,16 +277,6 @@ ROMClassBuilder::handleAnonClassName(J9CfrClassFile *classfile, ROMClassCreation
PORT_ACCESS_FROM_PORT(_portLibrary);

#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
/*
* Prevent generated LambdaForm classes from MethodHandles to be stored to the shared cache.
* When there are a large number of such classes in the shared cache, they trigger a lot of class comparisons.
* Performance can be much worse (compared to shared cache turned off).
*/
if (isLambdaFormClassName(originalStringBytes, originalStringLength, NULL/*deterministicPrefixLength*/)) {
context->addFindClassFlags(J9_FINDCLASS_FLAG_DO_NOT_SHARE);
context->addFindClassFlags(J9_FINDCLASS_FLAG_LAMBDAFORM);
}

#if JAVA_SPEC_VERSION >= 15
/* InjectedInvoker is a hidden class without the strong attribute set. It
* is created by MethodHandleImpl.makeInjectedInvoker on the OpenJDK side.
Expand Down Expand Up @@ -417,22 +407,6 @@ ROMClassBuilder::handleAnonClassName(J9CfrClassFile *classfile, ROMClassCreation
j9str_printf(PORTLIB, buf, ROM_ADDRESS_LENGTH + 1, ROM_ADDRESS_FORMAT, 0);
memcpy(constantPool[newUtfCPEntry].bytes + newHostPackageLength + originalStringLength + 1, buf, ROM_ADDRESS_LENGTH + 1);

/* Mark if the class is a Lambda class. */
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
if (!context->isLambdaFormClass()
&& isLambdaClassName(reinterpret_cast<const char *>(_anonClassNameBuffer),
newAnonClassNameLength - 1, NULL/*deterministicPrefixLength*/)
) {
context->addFindClassFlags(J9_FINDCLASS_FLAG_LAMBDA);
}
#else /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
if (isLambdaClassName(reinterpret_cast<const char *>(_anonClassNameBuffer),
newAnonClassNameLength - 1, NULL/*deterministicPrefixLength*/)
) {
context->addFindClassFlags(J9_FINDCLASS_FLAG_LAMBDA);
}
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */

/* search constantpool for all other identical classRefs. We have not actually
* tested this scenario as javac will not output more than one classRef or utfRef of the
* same kind.
Expand Down Expand Up @@ -590,18 +564,6 @@ ROMClassBuilder::prepareAndLaydown( BufferManager *bufferManager, ClassFileParse
getSizeInfo(context, &romClassWriter, &srpOffsetTable, &countDebugDataOutOfLine, &sizeInformation);

U_32 romSize = 0;
#if JAVA_SPEC_VERSION < 21
U_32 sizeToCompareForLambda = 0;
if (context->isLambdaClass()) {
/*
* romSize calculated from getSizeInfo() does not involve StringInternManager. It is only accurate for string intern disabled classes.
* Lambda classes in java 15 and up are strong hidden classes (defined with Option.STONG), which has the same lifecycle as its
* defining class loader. It is string intern enabled. So pass classFileSize instead of romSize to sizeToCompareForLambda.
*/
sizeToCompareForLambda = classFileOracle.getClassFileSize();
}
#endif /* JAVA_SPEC_VERSION < 21 */

if (context->shouldCompareROMClassForEquality()) {
ROMClassVerbosePhase v(context, CompareHashtableROMClass);

Expand All @@ -624,9 +586,6 @@ ROMClassBuilder::prepareAndLaydown( BufferManager *bufferManager, ClassFileParse
modifiers,
extraModifiers,
optionalFlags,
#if JAVA_SPEC_VERSION < 21
sizeToCompareForLambda,
#endif /* JAVA_SPEC_VERSION < 21 */
context)
) {
return OK;
Expand Down Expand Up @@ -720,9 +679,6 @@ ROMClassBuilder::prepareAndLaydown( BufferManager *bufferManager, ClassFileParse
modifiers,
extraModifiers,
optionalFlags,
#if JAVA_SPEC_VERSION < 21
sizeToCompareForLambda,
#endif /* JAVA_SPEC_VERSION < 21 */
context)

) {
Expand Down Expand Up @@ -1479,55 +1435,19 @@ ROMClassBuilder::compareROMClassForEquality(
U_32 modifiers,
U_32 extraModifiers,
U_32 optionalFlags,
#if JAVA_SPEC_VERSION < 21
U_32 sizeToCompareForLambda,
#endif /* JAVA_SPEC_VERSION < 21 */
ROMClassCreationContext *context)
{
bool ret = false;
ComparingCursor compareCursor(_javaVM, srpOffsetTable, srpKeyProducer, classFileOracle, romClass, romClassIsShared, context);
romClassWriter->writeROMClass(&compareCursor,
&compareCursor,
&compareCursor,
NULL,
NULL,
0, modifiers, extraModifiers, optionalFlags,
ROMClassWriter::WRITE);

#if JAVA_SPEC_VERSION < 21
if (context->isLambdaClass()) {
/*
* Lambda class names are in the format of HostClassName$$Lambda$<IndexNumber>/<zeroed out ROM_ADDRESS>.
* When we reach this check, the host class names will be the same for both the classes because
* of the earlier hash-key check. So, the only difference in the size will be the difference
* between the number of digits of the index number. The same lambda class might have a
* different index number from run to run and when the number of digits of the index number
* increases by 1, classFileSize also increases by 1. The indexNumber is the counter for the number of
* lambda classes defined so far. It is an int in the JCL side. So the it cannot vary more than max
* integer vs 0, which is maxVariance (9 bytes). This check is different than the romSize check because
* when the number of digits of the index number increases by 1, classFileSize also increases by 1 but
* romSize increases by 2.
*/
int maxVariance = 9;
int variance = abs(static_cast<int>((sizeToCompareForLambda - reinterpret_cast<J9ROMClass *>(romClass)->classFileSize)));
if (variance <= maxVariance) {
ComparingCursor compareCursor(_javaVM, srpOffsetTable, srpKeyProducer, classFileOracle, romClass, romClassIsShared, context);
romClassWriter->writeROMClass(&compareCursor,
&compareCursor,
&compareCursor,
NULL,
NULL,
0, modifiers, extraModifiers, optionalFlags,
ROMClassWriter::WRITE);

ret = compareCursor.isEqual();
}
} else
#endif /* JAVA_SPEC_VERSION < 21 */
{
ComparingCursor compareCursor(_javaVM, srpOffsetTable, srpKeyProducer, classFileOracle, romClass, romClassIsShared, context);
romClassWriter->writeROMClass(&compareCursor,
&compareCursor,
&compareCursor,
NULL,
NULL,
0, modifiers, extraModifiers, optionalFlags,
ROMClassWriter::WRITE);

ret = compareCursor.isEqual();
}
ret = compareCursor.isEqual();
J9UTF8* name = J9ROMCLASS_CLASSNAME((J9ROMClass *)romClass);
Trc_BCU_compareROMClassForEquality_event(ret, J9UTF8_LENGTH(name), J9UTF8_DATA(name));
return ret;
Expand Down
3 changes: 0 additions & 3 deletions runtime/bcutil/ROMClassBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,6 @@ class ROMClassBuilder
U_32 modifiers,
U_32 extraModifiers,
U_32 optionalFlags,
#if JAVA_SPEC_VERSION < 21
U_32 sizeToCompareForLambda,
#endif /* JAVA_SPEC_VERSION < 21 */
ROMClassCreationContext *context);

SharedCacheRangeInfo getSharedCacheSRPRangeInfo(void *address);
Expand Down
28 changes: 1 addition & 27 deletions runtime/bcutil/ROMClassCreationContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,6 @@ class ROMClassCreationContext
bool isHiddenClassOptNestmateSet() const { return J9_ARE_ALL_BITS_SET(_findClassFlags, J9_FINDCLASS_FLAG_CLASS_OPTION_NESTMATE); }
bool isHiddenClassOptStrongSet() const { return J9_ARE_ALL_BITS_SET(_findClassFlags, J9_FINDCLASS_FLAG_CLASS_OPTION_STRONG); }
bool isDoNotShareClassFlagSet() const {return J9_ARE_ALL_BITS_SET(_findClassFlags, J9_FINDCLASS_FLAG_DO_NOT_SHARE);}
bool isLambdaClass() const { return J9_ARE_ALL_BITS_SET(_findClassFlags, J9_FINDCLASS_FLAG_LAMBDA); }
#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)
bool isLambdaFormClass() const { return J9_ARE_ALL_BITS_SET(_findClassFlags, J9_FINDCLASS_FLAG_LAMBDAFORM); }
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */

bool isClassUnmodifiable() const {
bool unmodifiable = false;
Expand Down Expand Up @@ -406,33 +402,11 @@ class ROMClassCreationContext
if (NULL != _className) {
U_16 classNameLenToCompare0 = static_cast<U_16>(_classNameLength);
U_16 classNameLenToCompare1 = classNameLength;
BOOLEAN misMatch = FALSE;
if (isClassHidden()) {
/* For hidden classes, className has the ROM address appended, _className does not. */
classNameLenToCompare1 = static_cast<U_16>(_classNameLength);
#if JAVA_SPEC_VERSION < 21
if (isROMClassShareable()) {
/*
* Before JDK21, Lambda class names are in the format:
* HostClassName$$Lambda$<IndexNumber>/<zeroed out ROM_ADDRESS>
* Do not compare the IndexNumber as it can be different from run to run.
*/
U_8 *lambdaClass0 = reinterpret_cast<U_8 *>(getLastDollarSignOfLambdaClassName(
reinterpret_cast<const char *>(_className), _classNameLength));
U_8 *lambdaClass1 = reinterpret_cast<U_8 *>(getLastDollarSignOfLambdaClassName(
reinterpret_cast<const char *>(className), classNameLength));
if ((NULL != lambdaClass0) && (NULL != lambdaClass1)) {
classNameLenToCompare0 = static_cast<U_16>(lambdaClass0 - _className + 1);
classNameLenToCompare1 = static_cast<U_16>(lambdaClass1 - className + 1);
} else if ((NULL == lambdaClass0) != (NULL == lambdaClass1)) {
misMatch = TRUE;
}
}
#endif /* JAVA_SPEC_VERSION < 21 */
}
if (misMatch
|| !J9UTF8_DATA_EQUALS(_className, classNameLenToCompare0, className, classNameLenToCompare1)
) {
if (!J9UTF8_DATA_EQUALS(_className, classNameLenToCompare0, className, classNameLenToCompare1)) {
#define J9WRONGNAME " (wrong name: "
PORT_ACCESS_FROM_PORT(_portLibrary);
UDATA errorStringSize = _classNameLength + sizeof(J9WRONGNAME) + 1 + classNameLength;
Expand Down
3 changes: 3 additions & 0 deletions runtime/jcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ target_link_libraries(jclse
j9vm_interface
j9vm_gc_includes

j9shrcommon
j9shrutil
j9zip
omrsig
j9hookable
j9zlib
Expand Down
123 changes: 121 additions & 2 deletions runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@
#include "j9vmconstantpool.h"
#include "ObjectAccessBarrierAPI.hpp"
#include "objhelp.h"
#include "SCAbstractAPI.h"

#include <string.h>
#include <assert.h>

#undef UT_MODULE_LOADED
#undef UT_MODULE_UNLOADED
#include "CacheMap.hpp"
#include "VMHelpers.hpp"

extern "C" {
Expand Down Expand Up @@ -945,6 +949,121 @@ Java_java_lang_invoke_MethodHandleNatives_expand(JNIEnv *env, jclass clazz, jobj
vmFuncs->internalExitVMToJNI(currentThread);
}

/**
* Find a Lambda or customized LambdaForm class in the SCC.
* Requires non-null input arguments.
* If found, returns a pointer to a RAMClass of the SCC-cached class, otherwise nullptr.
*
* Throws OutOfMemoryError if lookup key memory allocation fails.
*/
jobject
findLambdaOrLambdaFormInSCC(JNIEnv *env, jclass clazz, jstring classnameObject, jclass lookupClass, UDATA options)
{
jobject result = nullptr;

#if defined(J9VM_OPT_SHARED_CLASSES)
J9VMThread *currentThread = reinterpret_cast<J9VMThread*>(env);
J9JavaVM *vm = currentThread->javaVM;
const J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
PORT_ACCESS_FROM_JAVAVM(vm);
vmFuncs->internalEnterVMFromJNI(currentThread);
Trc_JCL_java_lang_invoke_MethodHandleNatives_findLambdaOrLambdaFormInSCC_Entry(env, classnameObject, lookupClass, options);

#if defined(J9VM_ENV_DATA64)
#define J9_ROM_ADDRESS_SUFFIX "/0x0000000000000000"
#else /* defined(J9VM_ENV_DATA64) */
#define J9_ROM_ADDRESS_SUFFIX "/0x00000000"
#endif /* defined(J9VM_ENV_DATA64) */
j9object_t classname = J9_JNI_UNWRAP_REFERENCE(classnameObject);
IDATA classnameLength = vmFuncs->getStringUTF8Length(currentThread, classname);
UDATA romAddressSuffixLength = LITERAL_STRLEN(J9_ROM_ADDRESS_SUFFIX);
UDATA lookupKeyLength = classnameLength + romAddressSuffixLength + 1; /* +1 for null terminator. */
char *lookupKey = static_cast<char *>(j9mem_allocate_memory(lookupKeyLength, OMRMEM_CATEGORY_VM));

if (nullptr == lookupKey) {
vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);
} else {
vmFuncs->copyStringToUTF8Helper(currentThread, classname, J9_STR_NONE, 0, classnameLength, reinterpret_cast<U_8 *>(lookupKey), classnameLength);
memcpy(lookupKey + classnameLength, J9_ROM_ADDRESS_SUFFIX, romAddressSuffixLength);
lookupKey[lookupKeyLength - 1] = '\0';

if (nullptr != vm->sharedClassConfig && nullptr != vm->sharedClassConfig->sharedAPIObject) {
SCAbstractAPI *sharedapi = static_cast<SCAbstractAPI *>(vm->sharedClassConfig->sharedAPIObject);
J9ROMClass *romClass = sharedapi->jclFindOrphanROMClassByUniqueID(currentThread, lookupKey, lookupKeyLength - 1);
if (nullptr != romClass) {
J9Class *hostClass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, J9_JNI_UNWRAP_REFERENCE(lookupClass));
J9ClassLoader *classloader = hostClass->classLoader;
if (nullptr != hostClass) {
omrthread_monitor_enter(vm->classTableMutex);
/* internalCreateRAMClassFromROMClass will release the classTableMutex. */
J9Class *ramClass = vmFuncs->internalCreateRAMClassFromROMClass(
currentThread,
classloader,
romClass,
options,
nullptr /* elementClass */,
nullptr /* protectionDomain */,
nullptr /* methodRemapArray */,
J9_CP_INDEX_NONE,
LOAD_LOCATION_UNKNOWN,
nullptr /* classBeingRedefined */,
hostClass);
if (nullptr != ramClass && nullptr == currentThread->currentException) {
Trc_JCL_java_lang_invoke_MethodHandleNatives_findLambdaOrLambdaFormInSCC_Found(env, lookupKeyLength - 1, lookupKey, lookupClass, options);
result = vmFuncs->j9jni_createLocalRef(env, J9VM_J9CLASS_TO_HEAPCLASS(ramClass));
}
}
}
}
}

if (nullptr != lookupKey) {
j9mem_free_memory(lookupKey);
}
Trc_JCL_java_lang_invoke_MethodHandleNatives_findLambdaOrLambdaFormInSCC_Exit(env);
vmFuncs->internalExitVMToJNI(currentThread);
#undef J9_ROM_ADDRESS_SUFFIX
#endif /* J9VM_OPT_SHARED_CLASSES */
return result;
}

/**
* static native Class<?> findLambdaInSCC(String classname, Class<?> hostClass);
*
* Find a Lambda class in the SCC.
* Requires non-null input arguments.
* If found, returns a pointer to a RAMClass of the SCC-cached, otherwise nullptr.
*/
jobject JNICALL
Java_java_lang_invoke_MethodHandleNatives_findLambdaInSCC(JNIEnv *env, jclass clazz, jstring classnameObject, jclass lookupClass)
{
UDATA options = J9_FINDCLASS_FLAG_NO_CHECK_FOR_EXISTING_CLASS
| J9_FINDCLASS_FLAG_THROW_ON_FAIL
| J9_FINDCLASS_FLAG_UNSAFE
| J9_FINDCLASS_FLAG_HIDDEN
| J9_FINDCLASS_FLAG_CLASS_OPTION_NESTMATE
| J9_FINDCLASS_FLAG_CLASS_OPTION_STRONG;
return findLambdaOrLambdaFormInSCC(env, clazz, classnameObject, lookupClass, options);
}

/**
* static native Class<?> findLambdaFormInSCC(String classname, Class<?> hostClass);
*
* Find a customized LambdaForm class in the SCC.
* Requires non-null input arguments.
* If found, returns a pointer to a RAMClass of the SCC-cached class, otherwise nullptr.
*/
jobject JNICALL
Java_java_lang_invoke_MethodHandleNatives_findLambdaFormInSCC(JNIEnv *env, jclass clazz, jstring classnameObject, jclass lookupClass)
{
UDATA options = J9_FINDCLASS_FLAG_NO_CHECK_FOR_EXISTING_CLASS
| J9_FINDCLASS_FLAG_THROW_ON_FAIL
| J9_FINDCLASS_FLAG_UNSAFE
| J9_FINDCLASS_FLAG_HIDDEN
| J9_FINDCLASS_FLAG_ANON;
return findLambdaOrLambdaFormInSCC(env, clazz, classnameObject, lookupClass, options);
}

/**
* [JDK8] static native MemberName resolve(MemberName self, Class<?> caller)
* throws LinkageError, ClassNotFoundException;
Expand Down Expand Up @@ -987,7 +1106,7 @@ Java_java_lang_invoke_MethodHandleNatives_resolve(
char nameBuffer[256];
nameBuffer[0] = 0;
J9UTF8 *signature = NULL;
char signatureBuffer[256];
char signatureBuffer[512];
signatureBuffer[0] = 0;
PORT_ACCESS_FROM_JAVAVM(vm);
vmFuncs->internalEnterVMFromJNI(currentThread);
Expand Down Expand Up @@ -1419,7 +1538,7 @@ Java_java_lang_invoke_MethodHandleNatives_getMembers(
char nameBuffer[256];
nameBuffer[0] = 0;
J9UTF8 *signature = NULL;
char signatureBuffer[256];
char signatureBuffer[512];
signatureBuffer[0] = 0;
j9object_t callerObject = ((NULL == caller) ? NULL : J9_JNI_UNWRAP_REFERENCE(caller));

Expand Down
Loading

0 comments on commit 2b46b6c

Please sign in to comment.