先看代码
package com.zyt.jvmbook; public class Girl extends Person{ public Girl() { int a; } @Override public void sayHello() { System.out.println("girl say hello"); } private void sayHi(){ System.out.println("hellow"); } public static void main(String[] args) { Person p = new Girl(); p.sayHello(); Girl g=new Girl(); g.sayHello(); g.sayHi(); } }
查看编译后的内容
0 new #6 <com/zyt/jvmbook/Girl> 3 dup 4 invokespecial #7 <com/zyt/jvmbook/Girl.<init>> 7 astore_1 8 aload_1 9 invokevirtual #8 <com/zyt/jvmbook/Person.sayHello> 12 new #6 <com/zyt/jvmbook/Girl> 15 dup 16 invokespecial #7 <com/zyt/jvmbook/Girl.<init>> 19 astore_2 20 aload_2 21 invokevirtual #9 <com/zyt/jvmbook/Girl.sayHello> 24 aload_2 25 invokespecial #10 <com/zyt/jvmbook/Girl.sayHi> 28 return
看这个,对invoke孙ecial #7的解读
这个可真是费劲死了 弄了我两天,现在才搞清楚
之前的dup指令分析了是复制oop对象,那么此时操作数栈中是什么呢
///////高地址
oop
oop
//低地址
那么贴出invokespecail指令
---------------------------------------------------------------------- invokespecial 183 invokespecial [0x0000000002cf1da0, 0x0000000002cf2040] 672 bytes 0x0000000002cf1da0: push %rax 0x0000000002cf1da1: jmpq 0x0000000002cf1dd0 0x0000000002cf1da6: sub $0x8,%rsp 0x0000000002cf1daa: vmovss %xmm0,(%rsp) 0x0000000002cf1daf: jmpq 0x0000000002cf1dd0 0x0000000002cf1db4: sub $0x10,%rsp 0x0000000002cf1db8: vmovsd %xmm0,(%rsp) 0x0000000002cf1dbd: jmpq 0x0000000002cf1dd0 0x0000000002cf1dc2: sub $0x10,%rsp 0x0000000002cf1dc6: mov %rax,(%rsp) 0x0000000002cf1dca: jmpq 0x0000000002cf1dd0 0x0000000002cf1dcf: push %rax 开始执行.有栈顶缓存,即rax中有值 // void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, // int bcp_offset, // size_t index_size) { // assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); // if (index_size == sizeof(u2)) { // load_unsigned_short(index, Address(r13, bcp_offset));} 0x0000000002cf1dd0: mov %r13,-0x38(%rbp) //bcp的地址 0x0000000002cf1dd4: movzwl 0x1(%r13),%edx 0x0000000002cf1dd9: mov -0x28(%rbp),%rcx 将cache放到rcx 0x0000000002cf1ddd: shl $0x2,%edx //movl(bytecode, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); //那么cache就是rcx,index就是rdx 0x0000000002cf1de0: mov 0x10(%rcx,%rdx,8),%ebx 取得cache的_indices的, 0x10 + rcx + rdx*8 // const int shift_count = (1 + byte_no) * BitsPerByte; // shrl(bytecode, shift_count); 0x0000000002cf1de4: shr $0x10,%ebx 0x0000000002cf1de7: and $0xff,%ebx 0x0000000002cf1ded: cmp $0xb7,%ebx 0x0000000002cf1df3: je 0x0000000002cf1e9a //验证时否为0xb7 //在invokevirtual、invokespecial等字节码指令对应的汇编片段中,如果_indices中的b2或b1不为字节码指令的操作码, // 说明方法还没有连接,需要调用InterpreterRuntime::resolve_invoke()方法生成ConstantPoolCacheEntry。 ---解析ConstantPoolCacheEntry---开始 0x0000000002cf1df9: mov $0xb7,%ebx 0x0000000002cf1dfe: callq 0x0000000002cf1e08 0x0000000002cf1e03: jmpq 0x0000000002cf1e8e 0x0000000002cf1e08: mov %rbx,%rdx 0x0000000002cf1e0b: lea 0x8(%rsp),%rax 0x0000000002cf1e10: mov %r13,-0x38(%rbp) 0x0000000002cf1e14: mov %r15,%rcx 0x0000000002cf1e17: mov %rbp,0x1e8(%r15) 0x0000000002cf1e1e: mov %rax,0x1d8(%r15) 0x0000000002cf1e25: sub $0x20,%rsp 0x0000000002cf1e29: test $0xf,%esp 0x0000000002cf1e2f: je 0x0000000002cf1e47 0x0000000002cf1e35: sub $0x8,%rsp 0x0000000002cf1e39: callq 0x000000005449a3c0 0x0000000002cf1e3e: add $0x8,%rsp 0x0000000002cf1e42: jmpq 0x0000000002cf1e4c 0x0000000002cf1e47: callq 0x000000005449a3c0 0x0000000002cf1e4c: add $0x20,%rsp 0x0000000002cf1e50: movabs $0x0,%r10 0x0000000002cf1e5a: mov %r10,0x1d8(%r15) 0x0000000002cf1e61: movabs $0x0,%r10 0x0000000002cf1e6b: mov %r10,0x1e8(%r15) 0x0000000002cf1e72: cmpq $0x0,0x8(%r15) 0x0000000002cf1e7a: je 0x0000000002cf1e85 0x0000000002cf1e80: jmpq 0x0000000002cd07e0 0x0000000002cf1e85: mov -0x38(%rbp),%r13 0x0000000002cf1e89: mov -0x30(%rbp),%r14 0x0000000002cf1e8d: retq 0x0000000002cf1e8e: movzwl 0x1(%r13),%edx 0x0000000002cf1e93: mov -0x28(%rbp),%rcx 0x0000000002cf1e97: shl $0x2,%edx ---解析ConstantPoolCacheEntry---完成 //__ movptr(method, Address(cache, index, Address::times_ptr, method_offset)); //method_offset=24,取得method 0x0000000002cf1e9a: mov 0x18(%rcx,%rdx,8),%rbx // __ movl(flags, Address(cache, index, Address::times_ptr, flags_offset)); //取得flag //取得flag 0x0000000002cf1e9f: mov 0x28(%rcx,%rdx,8),%edx // if (load_receiver) { // __ movl(recv, flags); 0x0000000002cf1ea3: mov %edx,%ecx // __ andl(recv, ConstantPoolCacheEntry::parameter_size_mask); 0x0000000002cf1ea5: and $0xff,%ecx // __ movptr(recv, recv_addr); 0x0000000002cf1eab: mov -0x8(%rsp,%rcx,8),%rcx //-0x8 + rsp+ 8*rcx to rcx; // // compute return type //获取tosState (gdb) p/x _invoke_return_entry $1 = {0x7f1a6d006e80, 0x7f1a6d006f50, 0x7f1a6d007020, 0x7f1a6d0070f0, 0x7f1a6d0071c0, 0x7f1a6d007290, 0x7f1a6d007360, 0x7f1a6d007430, 0x7f1a6d007500} 0x0000000002cf1eb0: shr $0x1c,%edx 0x0000000002cf1eb3: movabs $0x54bcbba0,%r10 //_invoke_return_entry 0x0000000002cf1ebd: mov (%r10,%rdx,8),%rdx 获取返回entry // __ push(flags);// push return address 0x0000000002cf1ec1: push %rdx 0x0000000002cf1ec2: cmp (%rcx),%rax //cache, // movptr(mdp, Address(rbp, frame::interpreter_frame_mdx_offset * wordSize)); // testptr(mdp, mdp); // jcc(Assembler::zero, zero_continue); 0x0000000002cf1ec5: mov -0x20(%rbp),%rax //methodData 0x0000000002cf1ec9: test %rax,%rax 0x0000000002cf1ecc: je 0x0000000002cf1ee4 // increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); // // // The method data pointer needs to be updated to reflect the new target. // update_mdp_by_constant(mdp, in_bytes(CounterData::counter_data_size())); // bind(profile_continue); 0x0000000002cf1ed2: addq $0x1,0x8(%rax) 0x0000000002cf1ed7: sbbq $0x0,0x8(%rax) 0x0000000002cf1edc: add $0x10,%rax 0x0000000002cf1ee0: mov %rax,-0x20(%rbp) 0x0000000002cf1ee4: mov -0x20(%rbp),%rax 0x0000000002cf1ee8: test %rax,%rax 0x0000000002cf1eeb: je 0x0000000002cf2020 0x0000000002cf1ef1: cmpb $0xa,-0x10(%rax) 0x0000000002cf1ef5: jne 0x0000000002cf2020 0x0000000002cf1efb: add $0x8,%rax 0x0000000002cf1eff: mov -0x8(%rax),%r13 0x0000000002cf1f03: sub $0x0,%r13d 0x0000000002cf1f07: cmp $0x2,%r13d 0x0000000002cf1f0b: jl 0x0000000002cf2015 0x0000000002cf1f11: mov 0x8(%rbx),%r13 0x0000000002cf1f15: movzwl 0x2a(%r13),%r13d 0x0000000002cf1f1a: sub (%rax),%r13 0x0000000002cf1f1d: sub $0x1,%r13d 0x0000000002cf1f21: mov 0x8(%rsp,%r13,8),%r13 0x0000000002cf1f26: test %r13,%r13 0x0000000002cf1f29: jne 0x0000000002cf1f35 0x0000000002cf1f2b: orq $0x1,0x8(%rax) 0x0000000002cf1f33: jmp 0x0000000002cf1f82 0x0000000002cf1f35: mov 0x8(%r13),%r13d 0x0000000002cf1f39: shl $0x3,%r13 0x0000000002cf1f3d: xor 0x8(%rax),%r13 0x0000000002cf1f41: test $0xfffffffffffffffc,%r13 0x0000000002cf1f48: je 0x0000000002cf1f82 0x0000000002cf1f4a: test $0x2,%r13 0x0000000002cf1f51: jne 0x0000000002cf1f82 0x0000000002cf1f53: cmpq $0x0,0x8(%rax) 0x0000000002cf1f5b: je 0x0000000002cf1f7e 0x0000000002cf1f5d: cmpq $0x1,0x8(%rax) 0x0000000002cf1f65: je 0x0000000002cf1f7e 0x0000000002cf1f67: xor 0x8(%rax),%r13 0x0000000002cf1f6b: test $0xfffffffffffffffc,%r13 0x0000000002cf1f72: je 0x0000000002cf1f82 0x0000000002cf1f74: orq $0x2,0x8(%rax) 0x0000000002cf1f7c: jmp 0x0000000002cf1f82 0x0000000002cf1f7e: mov %r13,0x8(%rax) 0x0000000002cf1f82: add $0x10,%rax 0x0000000002cf1f86: mov -0x18(%rax),%r13 0x0000000002cf1f8a: sub $0x2,%r13d 0x0000000002cf1f8e: cmp $0x2,%r13d 0x0000000002cf1f92: jl 0x0000000002cf2015 0x0000000002cf1f98: mov 0x8(%rbx),%r13 0x0000000002cf1f9c: movzwl 0x2a(%r13),%r13d 0x0000000002cf1fa1: sub (%rax),%r13 0x0000000002cf1fa4: sub $0x1,%r13d 0x0000000002cf1fa8: mov 0x8(%rsp,%r13,8),%r13 0x0000000002cf1fad: test %r13,%r13 0x0000000002cf1fb0: jne 0x0000000002cf1fbc 0x0000000002cf1fb2: orq $0x1,0x8(%rax) 0x0000000002cf1fba: jmp 0x0000000002cf2009 0x0000000002cf1fbc: mov 0x8(%r13),%r13d 0x0000000002cf1fc0: shl $0x3,%r13 0x0000000002cf1fc4: xor 0x8(%rax),%r13 0x0000000002cf1fc8: test $0xfffffffffffffffc,%r13 0x0000000002cf1fcf: je 0x0000000002cf2009 0x0000000002cf1fd1: test $0x2,%r13 0x0000000002cf1fd8: jne 0x0000000002cf2009 0x0000000002cf1fda: cmpq $0x0,0x8(%rax) 0x0000000002cf1fe2: je 0x0000000002cf2005 0x0000000002cf1fe4: cmpq $0x1,0x8(%rax) 0x0000000002cf1fec: je 0x0000000002cf2005 0x0000000002cf1fee: xor 0x8(%rax),%r13 0x0000000002cf1ff2: test $0xfffffffffffffffc,%r13 0x0000000002cf1ff9: je 0x0000000002cf2009 0x0000000002cf1ffb: orq $0x2,0x8(%rax) 0x0000000002cf2003: jmp 0x0000000002cf2009 0x0000000002cf2005: mov %r13,0x8(%rax) 0x0000000002cf2009: add $0x10,%rax 0x0000000002cf200d: mov -0x28(%rax),%r13 0x0000000002cf2011: sub $0x4,%r13d 0x0000000002cf2015: shl $0x3,%r13d 0x0000000002cf2019: add %r13,%rax 0x0000000002cf201c: mov %rax,-0x20(%rbp) void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() { // set sender sp lea(r13, Address(rsp, wordSize)); // record last_sp movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), r13); } 0x0000000002cf2020: lea 0x8(%rsp),%r13 0x0000000002cf2025: mov %r13,-0x10(%rbp) jmp(Address(method, Method::from_interpreted_offset())); //jump到这里 volatile address _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry 0x0000000002cf2029: jmpq *0x50(%rbx) 0x0000000002cf202c: nopl 0x0(%rax) 0x0000000002cf2030: add %al,(%rax) 0x0000000002cf2032: add %al,(%rax) 0x0000000002cf2034: add %al,(%rax) 0x0000000002cf2036: add %al,(%rax) 0x0000000002cf2038: add %al,(%rax) 0x0000000002cf203a: add %al,(%rax) 0x0000000002cf203c: add %al,(%rax) 0x0000000002cf203e: add %al,(%rax) ----------------------------------------------------------------------
对应的代码为
void TemplateTable::invokespecial(int byte_no) { transition(vtos, vtos); assert(byte_no == f1_byte, "use this argument"); prepare_invoke(byte_no, rbx, noreg, // get f1 Method* rcx); // get receiver also for null check __ verify_oop(rcx); __ null_check(rcx); // do the call __ profile_call(rax); __ profile_arguments_type(rax, rbx, r13, false); __ jump_from_interpreted(rbx, rax); } void TemplateTable::prepare_invoke(int byte_no, Register method, // linked method (or i-klass) Register index, // itable index, MethodType, etc. Register recv, // if caller wants to see it Register flags // if caller wants to test it ) { // determine flags const Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; const bool is_invokedynamic = code == Bytecodes::_invokedynamic; const bool is_invokehandle = code == Bytecodes::_invokehandle; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; const bool load_receiver = (recv != noreg);//true const bool save_flags = (flags != noreg);//false assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), ""); assert(save_flags == (is_invokeinterface || is_invokevirtual), "need flags for vfinal"); assert(flags == noreg || flags == rdx, ""); assert(recv == noreg || recv == rcx, ""); // setup registers & access constant pool cache //recv=rcx,flags=noreg if (recv == noreg) recv = rcx;// if (flags == noreg) flags = rdx; assert_different_registers(method, index, recv, flags); // save 'interpreter return address' __ save_bcp(); load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); // maybe push appendix to arguments (just before return address) if (is_invokedynamic || is_invokehandle) { Label L_no_push; __ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift)); __ jcc(Assembler::zero, L_no_push); // Push the appendix as a trailing parameter. // This must be done before we get the receiver, // since the parameter_size includes it. __ push(rbx); __ mov(rbx, index); assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); __ load_resolved_reference_at_index(index, rbx); __ pop(rbx); __ push(index); // push appendix (MethodType, CallSite, etc.) __ bind(L_no_push); } // load receiver if needed (after appendix is pushed so parameter size is correct) // Note: no return address pushed yet if (load_receiver) {//true __ movl(recv, flags); __ andl(recv, ConstantPoolCacheEntry::parameter_size_mask); const int no_return_pc_pushed_yet = -1; // argument slot correction before we push return address const int receiver_is_at_end = -1; // back off one slot to get receiver Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end); __ movptr(recv, recv_addr); __ verify_oop(recv); } if (save_flags) {//false __ movl(r13, flags); } // compute return type __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift); // Make sure we don't need to mask flags after the above shift ConstantPoolCacheEntry::verify_tos_state_shift(); // load return address { const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); ExternalAddress table(table_addr); __ lea(rscratch1, table); __ movptr(flags, Address(rscratch1, flags, Address::times_ptr)); } // push return address __ push(flags); // Restore flags value from the constant pool cache, and restore rsi // for later null checks. r13 is the bytecode pointer if (save_flags) { __ movl(flags, r13); __ restore_bcp(); } } // void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, int bcp_offset, size_t index_size) { assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); if (index_size == sizeof(u2)) { load_unsigned_short(index, Address(r13, bcp_offset));} //返回 void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, int bcp_offset, size_t index_size) { assert_different_registers(cache, index); get_cache_index_at_bcp(index, bcp_offset, index_size); //将index返回值,即为index是bcp的地址的invoke指令的操作数 movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize));//将cache放入cache这个变量 assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index assert(exact_log2(in_words(ConstantPoolCacheEntry::size())) == 2, "else change next line"); shll(index, 2); } void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache, Register index, Register bytecode, int byte_no, int bcp_offset, size_t index_size) { get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size);//上边的执行到这里 // We use a 32-bit load here since the layout of 64-bit words on // little-endian machines allow us that. //cache 就是constanpoolcache,那么这个操作就是取得方法重写过后的方法所在常量池的序号index movl(bytecode, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); const int shift_count = (1 + byte_no) * BitsPerByte; assert((byte_no == TemplateTable::f1_byte && shift_count == ConstantPoolCacheEntry::bytecode_1_shift) || (byte_no == TemplateTable::f2_byte && shift_count == ConstantPoolCacheEntry::bytecode_2_shift), "correct shift count"); shrl(bytecode, shift_count);//右移16为 assert(ConstantPoolCacheEntry::bytecode_1_mask == ConstantPoolCacheEntry::bytecode_2_mask, "common mask"); andl(bytecode, ConstantPoolCacheEntry::bytecode_1_mask); } -------
看主要的逻辑,只有一个push,这个具体的和hsdb对不上,就先不追细节了
// __ push(flags);// push return address 0x0000000002cf1ec1: push %rdx
接着节点是
jmp(Address(method, Method::from_interpreted_offset())); //jump到这里 volatile address _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry 0x0000000002cf2029: jmpq *0x50(%rbx)
就是跳转到了method方法的_from_interpreted_entry
这个怎么来的呢?这个是在方法链接从时候设置的
void InstanceKlass::link_methods(TRAPS) { int len = methods()->length(); for (int i = len-1; i >= 0; i--) { methodHandle m(THREAD, methods()->at(i)); // Set up method entry points for compiler and interpreter . m->link_method(m, CHECK); // This is for JVMTI and unrelated to relocator but the last thing we do #ifdef ASSERT if (StressMethodComparator) { ResourceMark rm(THREAD); static int nmc = 0; for (int j = i; j >= 0 && j >= i-4; j--) { if ((++nmc % 1000) == 0) tty->print_cr("Have run MethodComparator %d times...", nmc); bool z = MethodComparator::methods_EMCP(m(), methods()->at(j)); if (j == i && !z) { tty->print("MethodComparator FAIL: "); m->print(); m->print_codes(); assert(z, "method must compare equal to itself"); } } } #endif //ASSERT } }
还有
// Called when the method_holder is getting linked. Setup entrypoints so the method // is ready to be called from interpreter, compiler, and vtables. void Method::link_method(methodHandle h_method, TRAPS) { // If the code cache is full, we may reenter this function for the // leftover methods that weren't linked. if (_i2i_entry != NULL) return; assert(_adapter == NULL, "init'd to NULL" ); assert( _code == NULL, "nothing compiled yet" ); // Setup interpreter entrypoint assert(this == h_method(), "wrong h_method()" ); address entry = Interpreter::entry_for_method(h_method); assert(entry != NULL, "interpreter entry must be non-null"); // Sets both _i2i_entry and _from_interpreted_entry set_interpreter_entry(entry); // Don't overwrite already registered native entries. if (is_native() && !has_native_function()) { set_native_function( SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), !native_bind_event_is_interesting); } // Setup compiler entrypoint. This is made eagerly, so we do not need // special handling of vtables. An alternative is to make adapters more // lazily by calling make_adapter() from from_compiled_entry() for the // normal calls. For vtable calls life gets more complicated. When a // call-site goes mega-morphic we need adapters in all methods which can be // called from the vtable. We need adapters on such methods that get loaded // later. Ditto for mega-morphic itable calls. If this proves to be a // problem we'll make these lazily later. (void) make_adapters(h_method, CHECK); // ONLY USE the h_method now as make_adapter may have blocked }
//以上是设置
static address entry_for_kind(MethodKind k) { assert(0 <= k && k < number_of_method_entries, "illegal kind"); return _entry_table[k]; } static address entry_for_method(methodHandle m) { return entry_for_kind(method_kind(m)); }
设置
void set_interpreter_entry(address entry) { _i2i_entry = entry; _from_interpreted_entry = entry; }
设置之前的method内容
方法的执行过程 enum MethodKind { zerolocals, // method needs locals initialization zerolocals_synchronized, // method needs locals initialization & is synchronized native, // native method native_synchronized, // native method & is synchronized empty, // empty method (code: _return) accessor, // accessor method (code: _aload_0, _getfield, _(a|i)return) abstract, // abstract method (throws an AbstractMethodException) method_handle_invoke_FIRST, // java.lang.invoke.MethodHandles::invokeExact, etc. method_handle_invoke_LAST = (method_handle_invoke_FIRST + (vmIntrinsics::LAST_MH_SIG_POLY - vmIntrinsics::FIRST_MH_SIG_POLY)), java_lang_math_sin, // implementation of java.lang.Math.sin (x) java_lang_math_cos, // implementation of java.lang.Math.cos (x) java_lang_math_tan, // implementation of java.lang.Math.tan (x) java_lang_math_abs, // implementation of java.lang.Math.abs (x) java_lang_math_sqrt, // implementation of java.lang.Math.sqrt (x) java_lang_math_log, // implementation of java.lang.Math.log (x) java_lang_math_log10, // implementation of java.lang.Math.log10 (x) java_lang_math_pow, // implementation of java.lang.Math.pow (x,y) java_lang_math_exp, // implementation of java.lang.Math.exp (x) java_lang_ref_reference_get, // implementation of java.lang.ref.Reference.get() java_util_zip_CRC32_update, // implementation of java.util.zip.CRC32.update() java_util_zip_CRC32_updateBytes, // implementation of java.util.zip.CRC32.updateBytes() java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer() number_of_method_entries, //26 invalid = -1 }; gdb) p _entry_table $7 = {0x7f671501e2e0 "H213S 20 17267J* 17267R(+с372366 01", 0x7f671501f060 "H213S 20 17267J* 17267R(+с372366 01", 0x7f6715028d40 "H213K 20 17267I*XL215t314370h", 0x7f671502a620 "H213K 20 17267I*XL215t314370h", 0x7f6715020180 "H213S 20 17267J* 17267R(+с372366 01", 0x7f6715020f00 "H213S 20 17267J* 17267R(+с372366 01", 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021c80 "H213e300H307", <incomplete sequence 360>, 0x7f6715021f80 "335D$335 05V200232 25331301331341337351335300331367 17207a", 0x7f6715022300 "335D$335 05326|232 25331301331341337351335300331367 17207a", 0x7f6715022680 "335D$335 05Vy232 25331301331341337351335300331367 17207 ", 0x7f6715022a00 "335D$331341H203354 20335 34$305373 20 04$H203304 20XI213345377340220220220220@", 0x7f6715022a40 "305373QD$XI213345377340220220220220314314314314314314314314314314314314314314314314@", 0x7f6715022a80 "335D$331355331311331361H203354 20335 34$305373 20 04$H203304 20XI213345377340@", 0x7f6715022ac0 "335D$331354331311331361H203354 20335 34$305373 20 04$H203304 20XI213345377340340 03", 0x7f6715022ee0 "335D$335D$ 30331356337351 17207}", 0x7f6715022b00 "335D$331300H203354233331<$213 04$201", <incomplete sequence 310>, 0x7f6715023420 "H213S 20 17267J* 17267R(+с372366 01", 0x7f67150241a0 "201=6C 01 26", 0x7f6715025ac0 "201= 26* 01 26", 0x7f6715027400 "201=326 20 01 26"} (gdb) p k $3 = AbstractInterpreter::zerolocals (gdb) p/d k $4 = 0 三层关系 -------------第一层----------------- (gdb) p h_method._value $7 = (Method *) 0x7f2908a13938 (gdb) p * h_method._value $8 = (Method) { <Metadata> = { <MetaspaceObj> = {<No data fields>}, members of Metadata: _vptr.Metadata = 0x7f291fcf1b90 <vtable for Method+16>, _valid = 0 }, members of Method: _constMethod = 0x7f2908a138c0, _method_data = 0x0, _method_counters = 0x0, _access_flags = { _flags = 138 }, _vtable_index = -3, _method_size = 12, _intrinsic_id = 0 '