写一个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
但是也没说明白调用链,开头就直接说了
给出以下逻辑
查看源码也是在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(); }
这下下就清楚了,这是气人,你重写代码,怎么不和我说一下呢