From f9c8af59c9e1affa34049540d30bedfb78a32937 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Sat, 31 Oct 2026 19:06:13 -0400 Subject: [PATCH] perf: skip Class.getName() when wrapping a returned object of a seen class Wrapping a Java object returned to JS called Class.getName() in Java, copied the jstring, and looked the class up again with FindClass for Link. ObjectManager now keeps up to four runtime classes per declared type, matched with IsSameObject (no Java code runs), and reuses the class name and global class. On a miss FindClass still runs after the wrapper is created, as before. --- .../runtime/src/main/cpp/ObjectManager.cpp | 36 ++++++++++++++----- test-app/runtime/src/main/cpp/ObjectManager.h | 13 +++++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/test-app/runtime/src/main/cpp/ObjectManager.cpp b/test-app/runtime/src/main/cpp/ObjectManager.cpp index 87a73caf0..0e0267056 100644 --- a/test-app/runtime/src/main/cpp/ObjectManager.cpp +++ b/test-app/runtime/src/main/cpp/ObjectManager.cpp @@ -290,24 +290,44 @@ Local ObjectManager::CreateJSWrapper(jint javaObjectID, JEnv env; JniLocalRef clazz(env.GetObjectClass(instance)); - return CreateJSWrapperHelper(javaObjectID, typeName, clazz); + auto& seen = m_wrapperClasses[typeName]; + for (const auto& entry : seen) { + if (env.isSameObject(entry.clazz, clazz)) { + return CreateJSWrapperHelper(javaObjectID, entry.className, entry.clazz); + } + } + + auto className = GetClassName((jclass)clazz); + jclass linked = nullptr; + auto jsWrapper = CreateJSWrapperHelper(javaObjectID, className, nullptr, &linked); + if (linked != nullptr) { + if (seen.size() >= 4) { + seen.erase(seen.begin()); + } + seen.push_back({linked, className}); + } + return jsWrapper; } Local ObjectManager::CreateJSWrapperHelper(jint javaObjectID, - const string& typeName, - jclass clazz) { + const string& className, + jclass globalClazz, + jclass* linkedClazz) { auto isolate = m_isolate; - auto className = (clazz != nullptr) ? GetClassName(clazz) : typeName; - auto node = MetadataNode::GetOrCreate(className); auto jsWrapper = node->CreateJSWrapper(isolate, this); if (!jsWrapper.IsEmpty()) { - JEnv env; - auto claz = env.FindClass(className); - Link(jsWrapper, javaObjectID, claz); + if (globalClazz == nullptr) { + JEnv env; + globalClazz = env.FindClass(className); + } + Link(jsWrapper, javaObjectID, globalClazz); + if (linkedClazz != nullptr) { + *linkedClazz = globalClazz; + } } return jsWrapper; } diff --git a/test-app/runtime/src/main/cpp/ObjectManager.h b/test-app/runtime/src/main/cpp/ObjectManager.h index 5fbe4afce..85c80bf61 100644 --- a/test-app/runtime/src/main/cpp/ObjectManager.h +++ b/test-app/runtime/src/main/cpp/ObjectManager.h @@ -212,8 +212,14 @@ class ObjectManager { DirectBuffer& inputBuff, DirectBuffer& outputBuff); v8::Local CreateJSWrapperHelper(jint javaObjectID, - const std::string& typeName, - jclass clazz); + const std::string& className, + jclass globalClazz, + jclass* linkedClazz = nullptr); + + struct WrapperClassEntry { + jclass clazz; + std::string className; + }; static void JSObjectWeakCallbackStatic( const v8::WeakCallbackInfo& data); @@ -253,6 +259,9 @@ class ObjectManager { std::unordered_map m_idToObject; + // Recent runtime classes per declared type. + std::unordered_map> m_wrapperClasses; + PersistentObjectIdSet m_released; std::set m_visited;