JMV对类的链接时,会对字节码进行重写,主要作用是实现常量池缓存,以及对方法的字节码重写,提高JVM字节码执行的性能。
bool InstanceKlass::link_class_impl(TRAPS) {
// 判断是否已经链接
if (is_linked()) {
return true;
}
JavaThread* jt = THREAD;
//首先链接所有的父类、
Klass* super_klass = super();
if (super_klass != nullptr) {
if (super_klass->is_interface()) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_IncompatibleClassChangeError(),
"class %s has interface %s as super class",
external_name(),
super_klass->external_name()
);
return false;
}
InstanceKlass* ik_super = InstanceKlass::cast(super_klass);
ik_super->link_class_impl(CHECK_false);
}
//然后链接所有的接口
Array<InstanceKlass*>* interfaces = local_interfaces();
int num_interfaces = interfaces->length();
for (int index = 0; index < num_interfaces; index++) {
InstanceKlass* interk = interfaces->at(index);
interk->link_class_impl(CHECK_false);
}
// 判断是否链接完成
if (is_linked()) {
return true;
}
// 验证并重写字节码
{
LockLinkState init_lock(this, jt);
// 判断是否重写完成
if (!is_linked()) {
if (!is_rewritten()) {
{
bool verify_ok = verify_code(THREAD);
if (!verify_ok) {
return false;
}
}
// 如果已经链接,直接借宿
if (is_linked()) {
return true;
}
//重新类的字节码
rewrite_class(CHECK_false);
}
//链接方法
link_methods(CHECK_false);
// 初始化vtable 和 itable
bool need_init_table = true;
if (need_init_table) { vtable().initialize_vtable_and_check_constraints(CHECK_false);
itable().initialize_itable_and_check_constraints(CHECK_false);
}
set_initialization_state_and_notify(linked, THREAD);
}
}
return true;
}
重新写类中所有方法的字节码,重写的时机发生在验证字节码之后,但是在第一个方法执行之前。
void InstanceKlass::rewrite_class(TRAPS) {
Rewriter::rewrite(this, CHECK);
set_rewritten();
}
重写主要是在常量池添加缓存,并重写方法的字节码指令。
void Rewriter::rewrite(InstanceKlass* klass, TRAPS) {
ResourceMark rm(THREAD);
constantPoolHandle cpool(THREAD, klass->constants());
Rewriter rw(klass, cpool, klass->methods(), CHECK);
}
Rewriter::Rewriter(InstanceKlass* klass, const constantPoolHandle& cpool, Array<Method*>* methods, TRAPS)
: _klass(klass),
_pool(cpool),
_methods(methods),
_cp_map(cpool->length()),
_cp_cache_map(cpool->length() / 2),
_reference_map(cpool->length()),
_resolved_references_map(cpool->length() / 2),
_invokedynamic_references_map(cpool->length() / 2),
_method_handle_invokers(cpool->length()),
_invokedynamic_index(0)
{
// 重写类字节码
rewrite_bytecodes(CHECK);
//添加常量池缓存
make_constant_pool_cache(THREAD);
// 重写方法中jsr字节码指令
int len = _methods->length();
for (int i = len-1; i >= 0; i--) {
methodHandle m(THREAD, _methods->at(i));
if (m->has_jsrs()) {
m = rewrite_jsrs(m, THREAD);
methods->at_put(i, m());
}
}
}
重写字节码逻辑如下 \
- 调用compute_index_maps方法计算索引映射。
- 重写java.lang.Object类的初始化的字节码指令。
- 重写方法的字节码指令。
void Rewriter::rewrite_bytecodes(TRAPS) {
assert(_pool->cache() == nullptr, "constant pool cache must not be set yet");
compute_index_maps();
if (RegisterFinalizersAtInit && _klass->name() == vmSymbols::java_lang_Object()) {
bool did_rewrite = false;
int i = _methods->length();
while (i-- > 0) {
Method* method = _methods->at(i);
if (method->intrinsic_id() == vmIntrinsics::_Object_init) {
methodHandle m(THREAD, method);
rewrite_Object_init(m, CHECK);
did_rewrite = true;
break;
}
}
assert(did_rewrite, "must find Object::<init> to rewrite it");
}
int len = _methods->length();
bool invokespecial_error = false;
for (int i = len-1; i >= 0; i--) {
Method* method = _methods->at(i);
scan_method(THREAD, method, false, &invokespecial_error);
if (invokespecial_error) {
THROW_MSG(vmSymbols::java_lang_InternalError(),
"This classfile overflows invokespecial for interfaces and cannot be loaded");
return;
}
}
}
计算常量池和缓存索引映射。
void Rewriter::compute_index_maps() {
const int length = _pool->length();
init_maps(length);
bool saw_mh_symbol = false;
for (int i = 0; i < length; i++) {
int tag = _pool->tag_at(i).value();
switch (tag) {
case JVM_CONSTANT_InterfaceMethodref:
case JVM_CONSTANT_Fieldref : // fall through
case JVM_CONSTANT_Methodref : // fall through
add_cp_cache_entry(i);
break;
case JVM_CONSTANT_Dynamic:
assert(_pool->has_dynamic_constant(), "constant pool's _has_dynamic_constant flag not set");
add_resolved_references_entry(i);
break;
case JVM_CONSTANT_String : // fall through
case JVM_CONSTANT_MethodHandle : // fall through
case JVM_CONSTANT_MethodType : // fall through
add_resolved_references_entry(i);
break;
case JVM_CONSTANT_Utf8:
if (_pool->symbol_at(i) == vmSymbols::java_lang_invoke_MethodHandle() ||
_pool->symbol_at(i) == vmSymbols::java_lang_invoke_VarHandle()) {
saw_mh_symbol = true;
}
break;
}
}
if (saw_mh_symbol) {
_method_handle_invokers.at_grow(length, 0);
}
}
添加常量池缓存如下 \
- 获取constantPool对应InstanceKlass的ClassLoaderData对象
- 分配ConstantPoolCache对象。
- 设置常量池缓存对象。
- 初始化已经解析的引用。
void Rewriter::make_constant_pool_cache(TRAPS) {
ClassLoaderData* loader_data = _pool->pool_holder()->class_loader_data();
ConstantPoolCache* cache =
ConstantPoolCache::allocate(loader_data, _cp_cache_map,
_invokedynamic_references_map, _initialized_indy_entries, CHECK);
// 设置常量池缓存对象
_pool->set_cache(cache);
cache->set_constant_pool(_pool());
//初始化已经解析的引用
_pool->initialize_resolved_references(loader_data, _resolved_references_map, _resolved_reference_limit,THREAD);
总结
本文主要分析了JVM对字节码进行重写,主要作用是实现常量池缓存,以及对方法的字节码重写,提高JVM字节码执行的性能。