jvm源码解读--11 ldc指令的解读

时间:2022-03-27 02:12:39

写一个java文件

    public static void main(String[] args) {
String str1="abc";
String str2 ="abc";
String str3=new String("abc");
boolean b1= str1==str2;
boolean b2= str1==str3; }

查看字节码code

 0 ldc #4 <abc>
2 astore_1
3 ldc #4 <abc>
5 astore_2
6 new #5 <java/lang/String>
9 dup
10 ldc #4 <abc>
12 invokespecial #6 <java/lang/String.<init>>
15 astore_3
16 aload_1
17 aload_2
18 if_acmpne 25 (+7)
21 iconst_1
22 goto 26 (+4)
25 iconst_0
26 istore 4
28 aload_1
29 aload_3
30 if_acmpne 37 (+7)
33 iconst_1
34 goto 38 (+4)
37 iconst_0
38 istore 5
40 getstatic #7 <java/lang/System.out>
43 ldc #8 <helloworld!>

能看待这个一个是ldc #4,其中#4的类型是

JVM_CONSTANT_String

然后对于String类的解析,会比较明白,先加载java/lang/String类,在生成oop对象,而ldc #4查看字节码的解析如下:

void TemplateTable::ldc(bool wide) {
transition(vtos, vtos);
Label call_ldc, notFloat, notClass, Done; if (wide) {
__ get_unsigned_2_byte_index_at_bcp(rbx, 1);
} else {
__ load_unsigned_byte(rbx, at_bcp(1));
}
__ get_cpool_and_tags(rcx, rax);
const int base_offset = ConstantPool::header_size() * wordSize;
const int tags_offset = Array<u1>::base_offset_in_bytes(); // get type
__ xorptr(rdx, rdx);
__ movb(rdx, Address(rax, rbx, Address::times_1, tags_offset)); // unresolved class - get the resolved class
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClass);
__ jccb(Assembler::equal, call_ldc); // unresolved class in error (resolution failed) - call into runtime
// so that the same error from first resolution attempt is thrown.
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError);
__ jccb(Assembler::equal, call_ldc); // resolved class - need to call vm to get java mirror of the class
__ cmpl(rdx, JVM_CONSTANT_Class);
__ jcc(Assembler::notEqual, notClass); __ bind(call_ldc);
__ movl(rcx, wide);
call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), rcx);
__ push(atos);
__ jmp(Done); __ bind(notClass);
__ cmpl(rdx, JVM_CONSTANT_Float);
__ jccb(Assembler::notEqual, notFloat);
// ftos
__ fld_s( Address(rcx, rbx, Address::times_ptr, base_offset));
__ push(ftos);
__ jmp(Done); __ bind(notFloat);
#ifdef ASSERT
{ Label L;
__ cmpl(rdx, JVM_CONSTANT_Integer);
__ jcc(Assembler::equal, L);
// String and Object are rewritten to fast_aldc
__ stop("unexpected tag type in ldc");
__ bind(L);
}
#endif
// itos JVM_CONSTANT_Integer only
__ movl(rax, Address(rcx, rbx, Address::times_ptr, base_offset));
__ push(itos);
__ bind(Done);
}

就算加上了汇编完成的东西

----------------------------------------------------------------------
ldc 18 ldc [0x00000000033c7840, 0x00000000033c79a0] 352 bytes 0x00000000033c7840: push %rax
0x00000000033c7841: jmpq 0x00000000033c7870
0x00000000033c7846: sub $0x8,%rsp
0x00000000033c784a: vmovss %xmm0,(%rsp)
0x00000000033c784f: jmpq 0x00000000033c7870
0x00000000033c7854: sub $0x10,%rsp
0x00000000033c7858: vmovsd %xmm0,(%rsp)
0x00000000033c785d: jmpq 0x00000000033c7870
0x00000000033c7862: sub $0x10,%rsp
0x00000000033c7866: mov %rax,(%rsp)
0x00000000033c786a: jmpq 0x00000000033c7870
0x00000000033c786f: push %rax
0x00000000033c7870: movzbl 0x1(%r13),%ebx
0x00000000033c7875: mov -0x18(%rbp),%rcx
0x00000000033c7879: mov 0x8(%rcx),%rcx
0x00000000033c787d: mov 0x8(%rcx),%rcx
0x00000000033c7881: mov 0x8(%rcx),%rax
0x00000000033c7885: movzbl 0x4(%rax,%rbx,1),%edx
// unresolved class - get the resolved class
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClass);
__ jccb(Assembler::equal, call_ldc);
0x00000000033c788a: cmp $0x64,%edx
0x00000000033c788d: je 0x00000000033c789d
// unresolved class in error state - call into runtime to throw the error
// from the first resolution attempt
__ cmpl(rdx, JVM_CONSTANT_UnresolvedClassInError);
__ jccb(Assembler::equal, call_ldc);
0x00000000033c788f: cmp $0x67,%edx
0x00000000033c7892: je 0x00000000033c789d
// resolved class - need to call vm to get java mirror of the class
__ cmpl(rdx, JVM_CONSTANT_Class);
__ jcc(Assembler::notEqual, notClass);
0x00000000033c7894: cmp $0x7,%edx
0x00000000033c7897: jne 0x00000000033c794d
__ movl(c_rarg1, wide); wide=false
0x00000000033c789d: mov $0x0,%edx
0x00000000033c78a2: callq 0x00000000033c78ac
0x00000000033c78a7: jmpq 0x00000000033c7947
0x00000000033c78ac: lea 0x8(%rsp),%rax
0x00000000033c78b1: mov %r13,-0x38(%rbp)
0x00000000033c78b5: mov %r15,%rcx
0x00000000033c78b8: mov %rbp,0x1e8(%r15)
0x00000000033c78bf: mov %rax,0x1d8(%r15)
0x00000000033c78c6: sub $0x20,%rsp
0x00000000033c78ca: test $0xf,%esp
0x00000000033c78d0: je 0x00000000033c78e8
0x00000000033c78d6: sub $0x8,%rsp
0x00000000033c78da: callq 0x000000006aad9cd0
0x00000000033c78df: add $0x8,%rsp
0x00000000033c78e3: jmpq 0x00000000033c78ed
0x00000000033c78e8: callq 0x000000006aad9cd0
0x00000000033c78ed: add $0x20,%rsp
0x00000000033c78f1: movabs $0x0,%r10
0x00000000033c78fb: mov %r10,0x1d8(%r15)
0x00000000033c7902: movabs $0x0,%r10
0x00000000033c790c: mov %r10,0x1e8(%r15)
0x00000000033c7913: cmpq $0x0,0x8(%r15)
0x00000000033c791b: je 0x00000000033c7926
0x00000000033c7921: jmpq 0x00000000033b07e0
0x00000000033c7926: mov 0x238(%r15),%rax
0x00000000033c792d: movabs $0x0,%r10
0x00000000033c7937: mov %r10,0x238(%r15)
0x00000000033c793e: mov -0x38(%rbp),%r13
0x00000000033c7942: mov -0x30(%rbp),%r14
0x00000000033c7946: retq
0x00000000033c7947: push %rax
0x00000000033c7948: jmpq 0x00000000033c796b
__ bind(notClass);
__ cmpl(rdx, JVM_CONSTANT_Float); 常量值为4
__ jccb(Assembler::notEqual, notFloat);
0x00000000033c794d: cmp $0x4,%edx
0x00000000033c7950: jne 0x00000000033c7966
0x00000000033c7952: vmovss 0x50(%rcx,%rbx,8),%xmm0
0x00000000033c7958: sub $0x8,%rsp
0x00000000033c795c: vmovss %xmm0,(%rsp)
0x00000000033c7961: jmpq 0x00000000033c796b __ bind(notFloat); 0x00000000033c7966: mov 0x50(%rcx,%rbx,8),%eax
0x00000000033c796a: push %rax //下一条指令取指
0x00000000033c796b: movzbl 0x2(%r13),%ebx
0x00000000033c7970: add $0x2,%r13
0x00000000033c7974: movabs $0x6b2143f0,%r10
0x00000000033c797e: jmpq *(%r10,%rbx,8)
0x00000000033c7982: nopw 0x0(%rax,%rax,1)
0x00000000033c7988: add %al,(%rax)
0x00000000033c798a: add %al,(%rax)
0x00000000033c798c: add %al,(%rax)
0x00000000033c798e: add %al,(%rax)
0x00000000033c7990: add %al,(%rax)
0x00000000033c7992: add %al,(%rax)
0x00000000033c7994: add %al,(%rax)
0x00000000033c7996: add %al,(%rax)
0x00000000033c7998: add %al,(%rax)
0x00000000033c799a: add %al,(%rax)
0x00000000033c799c: add %al,(%rax)
0x00000000033c799e: add %al,(%rax)
----------------------------------------------------------------------

也分析不出来,打断点始终打不到,对于ldc  加载类,却可以打到断点,真是迷惑,查了一圈看了一篇文章说会调用StringTable::intern

文章链接:iizhihu.com/question/60778124

但是也没说明白调用链,开头就直接说了

给出以下逻辑

jvm源码解读--11 ldc指令的解读

查看源码也是在bytecodeInterpreter.cpp找到了这段内容

      CASE(_ldc_w):
CASE(_ldc):
{
u2 index;
bool wide = false;
int incr = 2; // frequent case
if (opcode == Bytecodes::_ldc) {
index = pc[1];
} else {
index = Bytes::get_Java_u2(pc+1);
incr = 3;
wide = true;
} ConstantPool* constants = METHOD->constants();
switch (constants->tag_at(index).value()) {
case JVM_CONSTANT_Integer:
SET_STACK_INT(constants->int_at(index), 0);
break; case JVM_CONSTANT_Float:
SET_STACK_FLOAT(constants->float_at(index), 0);
break; case JVM_CONSTANT_String:
{
oop result = constants->resolved_references()->obj_at(index);
if (result == NULL) {
CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
SET_STACK_OBJECT(THREAD->vm_result(), 0);
THREAD->set_vm_result(NULL);
} else {
VERIFY_OOP(result);
SET_STACK_OBJECT(result, 0);
}
break;
} case JVM_CONSTANT_Class:
VERIFY_OOP(constants->resolved_klass_at(index)->java_mirror());
SET_STACK_OBJECT(constants->resolved_klass_at(index)->java_mirror(), 0);
break; case JVM_CONSTANT_UnresolvedClass:
case JVM_CONSTANT_UnresolvedClassInError:
CALL_VM(InterpreterRuntime::ldc(THREAD, wide), handle_exception);
SET_STACK_OBJECT(THREAD->vm_result(), 0);
THREAD->set_vm_result(NULL);
break; default: ShouldNotReachHere();
}
UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
}

这个在里面打不了断点呢,上便的逻辑一点都不清楚,如何是好? 这个cpp文件都是灰色的,最后有这个 #ifndef CC_INTERP ,感觉不是模板编译器,

那么,参考知乎的文章,打了断点 发现bytecode 是fast_aldc,并且要加载的字符串是"system",与ldc 指令对上了呢,没想明白怎么出来的一个_fast_ldc指令,对于指令的解析,已经看过了没有对应的Constant String ,tag保存的类型为08的,最后还是让我发现了,hotspot在方法链接的时候将ldc 重写成了_fast_aldc,自己没看方法重写的代码,不知道呢

为什么呢?其实这不怪你,因为这是在连接的时候重写了,自己没看这部分内容,看来还要补起来

// Rewrites a method given the index_map information
void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_error) {
..
case Bytecodes::_ldc:
case Bytecodes::_fast_aldc: // if reverse=true
maybe_rewrite_ldc(bcp, prefix_length+1, false, reverse);
..
} // Rewrite some ldc bytecodes to _fast_aldc
void Rewriter::maybe_rewrite_ldc(address bcp, int offset, bool is_wide,
bool reverse) {
if (!reverse) {
assert((*bcp) == (is_wide ? Bytecodes::_ldc_w : Bytecodes::_ldc), "not ldc bytecode");
address p = bcp + offset;
int cp_index = is_wide ? Bytes::get_Java_u2(p) : (u1)(*p);
constantTag tag = _pool->tag_at(cp_index).value();
/**
* bool is_method_handle() const { return _tag == JVM_CONSTANT_MethodHandle; }
* bool is_method_type() const { return _tag == JVM_CONSTANT_MethodType; }
* bool is_string() const { return _tag == JVM_CONSTANT_String; }
*/ if (tag.is_method_handle() || tag.is_method_type() || tag.is_string()) { int ref_index = cp_entry_to_resolved_references(cp_index);
if (is_wide) {
(*bcp) = Bytecodes::_fast_aldc_w;
assert(ref_index == (u2)ref_index, "index overflow");
Bytes::put_native_u2(p, ref_index);
} else {
(*bcp) = Bytecodes::_fast_aldc;//将ldc转化成_fast_aldc,这个你不看到这里,怎么也跟踪不到呢??
assert(ref_index == (u1)ref_index, "index overflow");
(*p) = (u1)ref_index;
}
}
} else {
Bytecodes::Code rewritten_bc =
(is_wide ? Bytecodes::_fast_aldc_w : Bytecodes::_fast_aldc);
if ((*bcp) == rewritten_bc) {
address p = bcp + offset;
int ref_index = is_wide ? Bytes::get_native_u2(p) : (u1)(*p);
int pool_index = resolved_references_entry_to_pool_index(ref_index);
if (is_wide) {
(*bcp) = Bytecodes::_ldc_w;
assert(pool_index == (u2)pool_index, "index overflow");
Bytes::put_Java_u2(p, pool_index);
} else {
(*bcp) = Bytecodes::_ldc;
assert(pool_index == (u1)pool_index, "index overflow");
(*p) = (u1)pool_index;
}
}
}
}

接着就是

那看ldc和fast_aldc的汇编器,这里面写着生成oop对象

// Fast path for caching oop constants.
void TemplateTable::fast_aldc(bool wide) {
transition(vtos, atos); Register result = rax;
Register tmp = rdx;
int index_size = wide ? sizeof(u2) : sizeof(u1); Label resolved; // We are resolved if the resolved reference cache entry contains a
// non-null object (String, MethodType, etc.)
assert_different_registers(result, tmp);
__ get_cache_index_at_bcp(tmp, 1, index_size);
__ load_resolved_reference_at_index(result, tmp);
__ testl(result, result);
__ jcc(Assembler::notZero, resolved); address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); // first time invocation - must resolve first
__ movl(tmp, (int)bytecode());
__ call_VM(result, entry, tmp); __ bind(resolved); if (VerifyOops) {
__ verify_oop(result);
}
}

如下

oop Bytecode_loadconstant::resolve_constant(TRAPS) const {
assert(_method.not_null(), "must supply method to resolve constant");
int index = raw_index();
ConstantPool* constants = _method->constants();
if (has_cache_index()) {
return constants->resolve_cached_constant_at(index, THREAD);
} else {
return constants->resolve_constant_at(index, THREAD);
}
}
---------------
oop resolve_cached_constant_at(int cache_index, TRAPS) {
constantPoolHandle h_this(THREAD, this);
return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, THREAD);
}
---------------
// Called to resolve constants in the constant pool and return an oop.
// Some constant pool entries cache their resolved oop. This is also
// called to create oops from constants to use in arguments for invokedynamic
oop ConstantPool::resolve_constant_at_impl(constantPoolHandle this_oop, int index, int cache_index, TRAPS) {
oop result_oop = NULL;
Handle throw_exception; if (cache_index == _possible_index_sentinel) {
// It is possible that this constant is one which is cached in the objects.
// We'll do a linear search. This should be OK because this usage is rare.
assert(index > 0, "valid index");
cache_index = this_oop->cp_to_object_index(index);
}
assert(cache_index == _no_index_sentinel || cache_index >= 0, "");
assert(index == _no_index_sentinel || index >= 0, ""); if (cache_index >= 0) {
result_oop = this_oop->resolved_references()->obj_at(cache_index);
if (result_oop != NULL) {
return result_oop;
// That was easy...
}
index = this_oop->object_to_cp_index(cache_index);
} jvalue prim_value; // temp used only in a few cases below int tag_value = this_oop->tag_at(index).value(); switch (tag_value) { case JVM_CONSTANT_UnresolvedClass:
case JVM_CONSTANT_UnresolvedClassInError:
case JVM_CONSTANT_Class:
{
assert(cache_index == _no_index_sentinel, "should not have been set");
Klass* resolved = klass_at_impl(this_oop, index, CHECK_NULL);
// ldc wants the java mirror.
result_oop = resolved->java_mirror();
break;
} case JVM_CONSTANT_String:
assert(cache_index != _no_index_sentinel, "should have been set");
if (this_oop->is_pseudo_string_at(index)) {
result_oop = this_oop->pseudo_string_at(index, cache_index);
break;
}
result_oop = string_at_impl(this_oop, index, cache_index, CHECK_NULL);
break; case JVM_CONSTANT_MethodHandleInError:
case JVM_CONSTANT_MethodTypeInError:
{
Symbol* error = SystemDictionary::find_resolution_error(this_oop, index);
guarantee(error != (Symbol*)NULL, "tag mismatch with resolution error table");
ResourceMark rm;
THROW_MSG_0(error, "");
break;
} case JVM_CONSTANT_MethodHandle:
{
int ref_kind = this_oop->method_handle_ref_kind_at(index);
int callee_index = this_oop->method_handle_klass_index_at(index);
Symbol* name = this_oop->method_handle_name_ref_at(index);
Symbol* signature = this_oop->method_handle_signature_ref_at(index);
if (PrintMiscellaneous)
tty->print_cr("resolve JVM_CONSTANT_MethodHandle:%d [%d/%d/%d] %s.%s",
ref_kind, index, this_oop->method_handle_index_at(index),
callee_index, name->as_C_string(), signature->as_C_string());
KlassHandle callee;
{ Klass* k = klass_at_impl(this_oop, callee_index, CHECK_NULL);
callee = KlassHandle(THREAD, k);
}
KlassHandle klass(THREAD, this_oop->pool_holder());
Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,
callee, name, signature,
THREAD);
result_oop = value();
if (HAS_PENDING_EXCEPTION) {
save_and_throw_exception(this_oop, index, tag_value, CHECK_NULL);
}
break;
} case JVM_CONSTANT_MethodType:
{
Symbol* signature = this_oop->method_type_signature_at(index);
if (PrintMiscellaneous)
tty->print_cr("resolve JVM_CONSTANT_MethodType [%d/%d] %s",
index, this_oop->method_type_index_at(index),
signature->as_C_string());
KlassHandle klass(THREAD, this_oop->pool_holder());
Handle value = SystemDictionary::find_method_handle_type(signature, klass, THREAD);
result_oop = value();
if (HAS_PENDING_EXCEPTION) {
save_and_throw_exception(this_oop, index, tag_value, CHECK_NULL);
}
break;
} case JVM_CONSTANT_Integer:
assert(cache_index == _no_index_sentinel, "should not have been set");
prim_value.i = this_oop->int_at(index);
result_oop = java_lang_boxing_object::create(T_INT, &prim_value, CHECK_NULL);
break; case JVM_CONSTANT_Float:
assert(cache_index == _no_index_sentinel, "should not have been set");
prim_value.f = this_oop->float_at(index);
result_oop = java_lang_boxing_object::create(T_FLOAT, &prim_value, CHECK_NULL);
break; case JVM_CONSTANT_Long:
assert(cache_index == _no_index_sentinel, "should not have been set");
prim_value.j = this_oop->long_at(index);
result_oop = java_lang_boxing_object::create(T_LONG, &prim_value, CHECK_NULL);
break; case JVM_CONSTANT_Double:
assert(cache_index == _no_index_sentinel, "should not have been set");
prim_value.d = this_oop->double_at(index);
result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL);
break; default:
DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d",
this_oop(), index, cache_index, tag_value) );
assert(false, "unexpected constant tag");
break;
} if (cache_index >= 0) {
// Cache the oop here also.
Handle result_handle(THREAD, result_oop);
MonitorLockerEx ml(this_oop->lock()); // don't know if we really need this
oop result = this_oop->resolved_references()->obj_at(cache_index);
// Benign race condition: resolved_references may already be filled in while we were trying to lock.
// The important thing here is that all threads pick up the same result.
// It doesn't matter which racing thread wins, as long as only one
// result is used by all threads, and all future queries.
// That result may be either a resolved constant or a failure exception.
if (result == NULL) {
this_oop->resolved_references()->obj_at_put(cache_index, result_handle());
return result_handle();
} else {
// Return the winning thread's result. This can be different than
// result_handle() for MethodHandles.
return result;
}
} else {
return result_oop;
}
}
-------------------
oop ConstantPool::string_at_impl(constantPoolHandle this_oop, int which, int obj_index, TRAPS) {
// If the string has already been interned, this entry will be non-null
oop str = this_oop->resolved_references()->obj_at(obj_index);
if (str != NULL) return str;
Symbol* sym = this_oop->unresolved_string_at(which);
str = StringTable::intern(sym, CHECK_(NULL));
this_oop->string_at_put(which, obj_index, str);
assert(java_lang_String::is_instance(str), "must be string");
return str;
}
-----
oop StringTable::intern(Symbol* symbol, TRAPS) {
if (symbol == NULL) return NULL;
ResourceMark rm(THREAD);
int length;
jchar* chars = symbol->as_unicode(length);
Handle string;
oop result = intern(string, chars, length, CHECK_NULL);
return result;
}
-----
oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
unsigned int hashValue = hash_string(name, len);
int index = the_table()->hash_to_index(hashValue);
oop found_string = the_table()->lookup(index, name, len, hashValue); // Found
if (found_string != NULL) return found_string; debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
assert(!Universe::heap()->is_in_reserved(name),
"proposed name of symbol must be stable"); Handle string;
// try to reuse the string if possible
if (!string_or_null.is_null()) {
string = string_or_null;
} else {
string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
} // Grab the StringTable_lock before getting the_table() because it could
// change at safepoint.
MutexLocker ml(StringTable_lock, THREAD); // Otherwise, add to symbol to table
return the_table()->basic_add(index, string, name, len,
hashValue, CHECK_NULL);
}
-----
oop StringTable::basic_add(int index_arg, Handle string, jchar* name,
int len, unsigned int hashValue_arg, TRAPS) { assert(java_lang_String::equals(string(), name, len),
"string must be properly initialized");
// Cannot hit a safepoint in this function because the "this" pointer can move.
No_Safepoint_Verifier nsv; // Check if the symbol table has been rehashed, if so, need to recalculate
// the hash value and index before second lookup.
unsigned int hashValue;
int index;
if (use_alternate_hashcode()) {
hashValue = hash_string(name, len);
index = hash_to_index(hashValue);
} else {
hashValue = hashValue_arg;
index = index_arg;
} // Since look-up was done lock-free, we need to check if another
// thread beat us in the race to insert the symbol. oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int)
if (test != NULL) {
// Entry already added
return test;
} HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string());
add_entry(index, entry);
return string();
}

这下下就清楚了,这是气人,你重写代码,怎么不和我说一下呢