jdk/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
Ioi Lam 0867f9b1b4 8377307: Refactor code for AOT cache pointer compression
Reviewed-by: jsjolen, xuelei, asmehra
2026-02-11 23:00:50 +00:00

335 lines
14 KiB
C++

/*
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
#define SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
#include "cds/aotCompressedPointers.hpp"
#include "cds/aotMetaspace.hpp"
#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "memory/metaspaceClosure.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashTable.hpp"
// This file contains *legacy* optimization for lambdas before JEP 483. May be removed in the future.
//
// The functionalties in this file are used only when CDSConfig::is_dumping_lambdas_in_legacy_mode()
// returns true during the creation of a CDS archive.
//
// With the legacy optimization, generated lambda proxy classes (with names such as
// java.util.ResourceBundle$Control$$Lambda/0x80000001d) are stored inside the CDS archive, accessible
// by LambdaProxyClassDictionary::find_proxy_class(). This saves part of the time for resolving a
// lambda call site (proxy class generation). However, a significant portion of the cost of
// the lambda call site resolution still remains in the production run.
//
// In contrast, with JEP 483, the entire lambda call site (starting from the constant pool entry), is
// resolved in the AOT cache assembly phase. No extra resolution is needed in the production run.
class InstanceKlass;
class Method;
class MetaspaceClosure;
class Symbol;
class outputStream;
class LambdaProxyClassKey {
InstanceKlass* _caller_ik;
Symbol* _invoked_name;
Symbol* _invoked_type;
Symbol* _method_type;
Method* _member_method;
Symbol* _instantiated_method_type;
public:
LambdaProxyClassKey(InstanceKlass* caller_ik,
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type) :
_caller_ik(caller_ik),
_invoked_name(invoked_name),
_invoked_type(invoked_type),
_method_type(method_type),
_member_method(member_method),
_instantiated_method_type(instantiated_method_type) {}
void metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_caller_ik);
it->push(&_invoked_name);
it->push(&_invoked_type);
it->push(&_method_type);
it->push(&_member_method);
it->push(&_instantiated_method_type);
}
bool equals(LambdaProxyClassKey const& other) const {
return _caller_ik == other._caller_ik &&
_invoked_name == other._invoked_name &&
_invoked_type == other._invoked_type &&
_method_type == other._method_type &&
_member_method == other._member_method &&
_instantiated_method_type == other._instantiated_method_type;
}
unsigned int hash() const;
static unsigned int dumptime_hash(Symbol* sym) {
if (sym == nullptr) {
// _invoked_name maybe null
return 0;
}
return java_lang_String::hash_code((const jbyte*)sym->bytes(), sym->utf8_length());
}
unsigned int dumptime_hash() const {
return dumptime_hash(_caller_ik->name()) +
dumptime_hash(_invoked_name) +
dumptime_hash(_invoked_type) +
dumptime_hash(_method_type) +
dumptime_hash(_instantiated_method_type);
}
static inline unsigned int DUMPTIME_HASH(LambdaProxyClassKey const& key) {
return (key.dumptime_hash());
}
static inline bool DUMPTIME_EQUALS(
LambdaProxyClassKey const& k1, LambdaProxyClassKey const& k2) {
return (k1.equals(k2));
}
InstanceKlass* caller_ik() const { return _caller_ik; }
Symbol* invoked_name() const { return _invoked_name; }
Symbol* invoked_type() const { return _invoked_type; }
Symbol* method_type() const { return _method_type; }
Method* member_method() const { return _member_method; }
Symbol* instantiated_method_type() const { return _instantiated_method_type; }
#ifndef PRODUCT
void print_on(outputStream* st) const;
#endif
};
class RunTimeLambdaProxyClassKey {
using narrowPtr = AOTCompressedPointers::narrowPtr;
narrowPtr _caller_ik;
narrowPtr _invoked_name;
narrowPtr _invoked_type;
narrowPtr _method_type;
narrowPtr _member_method;
narrowPtr _instantiated_method_type;
RunTimeLambdaProxyClassKey(narrowPtr caller_ik,
narrowPtr invoked_name,
narrowPtr invoked_type,
narrowPtr method_type,
narrowPtr member_method,
narrowPtr instantiated_method_type) :
_caller_ik(caller_ik),
_invoked_name(invoked_name),
_invoked_type(invoked_type),
_method_type(method_type),
_member_method(member_method),
_instantiated_method_type(instantiated_method_type) {}
public:
static RunTimeLambdaProxyClassKey init_for_dumptime(LambdaProxyClassKey& key) {
narrowPtr caller_ik = AOTCompressedPointers::encode_not_null(key.caller_ik());
narrowPtr invoked_name = AOTCompressedPointers::encode_not_null(key.invoked_name());
narrowPtr invoked_type = AOTCompressedPointers::encode_not_null(key.invoked_type());
narrowPtr method_type = AOTCompressedPointers::encode_not_null(key.method_type());
narrowPtr member_method = AOTCompressedPointers::encode(key.member_method()); // could be null
narrowPtr instantiated_method_type = AOTCompressedPointers::encode_not_null(key.instantiated_method_type());
return RunTimeLambdaProxyClassKey(caller_ik, invoked_name, invoked_type, method_type,
member_method, instantiated_method_type);
}
static RunTimeLambdaProxyClassKey init_for_runtime(InstanceKlass* caller_ik,
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type) {
// All parameters must be in shared space, or else you'd get an assert in
// ArchiveUtils::to_offset().
return RunTimeLambdaProxyClassKey(AOTCompressedPointers::encode_address_in_cache(caller_ik),
AOTCompressedPointers::encode_address_in_cache(invoked_name),
AOTCompressedPointers::encode_address_in_cache(invoked_type),
AOTCompressedPointers::encode_address_in_cache(method_type),
AOTCompressedPointers::encode_address_in_cache_or_null(member_method), // could be null
AOTCompressedPointers::encode_address_in_cache(instantiated_method_type));
}
unsigned int hash() const;
bool equals(RunTimeLambdaProxyClassKey const& other) const {
return _caller_ik == other._caller_ik &&
_invoked_name == other._invoked_name &&
_invoked_type == other._invoked_type &&
_method_type == other._method_type &&
_member_method == other._member_method &&
_instantiated_method_type == other._instantiated_method_type;
}
#ifndef PRODUCT
void print_on(outputStream* st) const;
#endif
};
class DumpTimeLambdaProxyClassInfo {
public:
GrowableArray<InstanceKlass*>* _proxy_klasses;
DumpTimeLambdaProxyClassInfo() : _proxy_klasses(nullptr) {}
DumpTimeLambdaProxyClassInfo& operator=(const DumpTimeLambdaProxyClassInfo&) = delete;
~DumpTimeLambdaProxyClassInfo();
void add_proxy_klass(InstanceKlass* proxy_klass) {
if (_proxy_klasses == nullptr) {
_proxy_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(5, mtClassShared);
}
assert(_proxy_klasses != nullptr, "sanity");
_proxy_klasses->append(proxy_klass);
}
void metaspace_pointers_do(MetaspaceClosure* it) {
for (int i=0; i<_proxy_klasses->length(); i++) {
it->push(_proxy_klasses->adr_at(i));
}
}
};
class RunTimeLambdaProxyClassInfo {
RunTimeLambdaProxyClassKey _key;
InstanceKlass* _proxy_klass_head;
public:
RunTimeLambdaProxyClassInfo(RunTimeLambdaProxyClassKey key, InstanceKlass* proxy_klass_head) :
_key(key), _proxy_klass_head(proxy_klass_head) {}
InstanceKlass* proxy_klass_head() const { return _proxy_klass_head; }
// Used by LambdaProxyClassDictionary to implement OffsetCompactHashtable::EQUALS
static inline bool EQUALS(
const RunTimeLambdaProxyClassInfo* value, RunTimeLambdaProxyClassKey* key, int len_unused) {
return (value->_key.equals(*key));
}
void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info);
unsigned int hash() const {
return _key.hash();
}
RunTimeLambdaProxyClassKey key() const {
return _key;
}
#ifndef PRODUCT
void print_on(outputStream* st) const;
#endif
};
class DumpTimeLambdaProxyClassDictionary
: public HashTable<LambdaProxyClassKey,
DumpTimeLambdaProxyClassInfo,
137, // prime number
AnyObj::C_HEAP,
mtClassShared,
LambdaProxyClassKey::DUMPTIME_HASH,
LambdaProxyClassKey::DUMPTIME_EQUALS> {
public:
DumpTimeLambdaProxyClassDictionary() : _count(0) {}
int _count;
};
// *Legacy* optimization for lambdas before JEP 483. May be removed in the future.
class LambdaProxyClassDictionary : public OffsetCompactHashtable<
RunTimeLambdaProxyClassKey*,
const RunTimeLambdaProxyClassInfo*,
RunTimeLambdaProxyClassInfo::EQUALS>
{
private:
class CleanupDumpTimeLambdaProxyClassTable;
static DumpTimeLambdaProxyClassDictionary* _dumptime_table;
static LambdaProxyClassDictionary _runtime_static_table; // for static CDS archive
static LambdaProxyClassDictionary _runtime_dynamic_table; // for dynamic CDS archive
static void add_to_dumptime_table(LambdaProxyClassKey& key,
InstanceKlass* proxy_klass);
static InstanceKlass* find_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info);
static InstanceKlass* find_lambda_proxy_class(InstanceKlass* caller_ik,
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type);
static InstanceKlass* load_and_init_lambda_proxy_class(InstanceKlass* lambda_ik,
InstanceKlass* caller_ik, TRAPS);
static void reset_registered_lambda_proxy_class(InstanceKlass* ik);
static InstanceKlass* get_shared_nest_host(InstanceKlass* lambda_ik);
public:
static void dumptime_init();
static void dumptime_classes_do(MetaspaceClosure* it);
static void add_lambda_proxy_class(InstanceKlass* caller_ik,
InstanceKlass* lambda_ik,
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type,
TRAPS);
static bool is_supported_invokedynamic(BootstrapInfo* bsi);
static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* caller_ik,
Symbol* invoked_name,
Symbol* invoked_type,
Symbol* method_type,
Method* member_method,
Symbol* instantiated_method_type,
TRAPS);
static void write_dictionary(bool is_static_archive);
static void adjust_dumptime_table();
static void cleanup_dumptime_table();
static void reset_dictionary(bool is_static_archive) {
if (is_static_archive) {
_runtime_static_table.reset();
} else {
_runtime_dynamic_table.reset();
}
}
static void serialize(SerializeClosure* soc, bool is_static_archive) {
if (is_static_archive) {
_runtime_static_table.serialize_header(soc);
} else {
_runtime_dynamic_table.serialize_header(soc);
}
}
static void print_on(const char* prefix, outputStream* st,
int start_index, bool is_static_archive);
static void print_statistics(outputStream* st, bool is_static_archive);
};
#endif // SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP