Java加載與存儲指令之ldc與_fast_aldc指令
ldc指令可以加載String
、方法類型或方法句柄的符號引用,但是如果要加載String、方法類型或方法句柄的符號引用,則會在類連接過程中重寫ldc字節(jié)碼指令為虛擬機內(nèi)部使用的字節(jié)碼指令_fast_aldc
。下面我們詳細介紹ldc指令如何加載int、float
類型和類類型的數(shù)據(jù),以及_fast_aldc
加載String、
方法類型或方法句柄,還有為什么要進行字節(jié)碼重寫等問題。
1、ldc字節(jié)碼指令
ldc指令將int、float或String型常量值從常量池中推送至棧頂。模板的定義如下:
def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , false );
ldc字節(jié)碼指令的格式如下:
// index是一個無符號的byte類型數(shù)據(jù),指明當(dāng)前類的運行時常量池的索引 ldc index
調(diào)用生成函數(shù)TemplateTable::ldc(bool wide
)。函數(shù)生成的匯編代碼如下:
第1部分代碼:
// movzbl指令負責(zé)拷貝一個字節(jié),并用0填充其目 // 的操作數(shù)中的其余各位,這種擴展方式叫"零擴展" // ldc指定的格式為ldc index,index為一個字節(jié) 0x00007fffe1028530: movzbl 0x1(%r13),%ebx // 加載index到%ebx // %rcx指向緩存池首地址、%rax指向類型數(shù)組_tags首地址 0x00007fffe1028535: mov -0x18(%rbp),%rcx 0x00007fffe1028539: mov 0x10(%rcx),%rcx 0x00007fffe102853d: mov 0x8(%rcx),%rcx 0x00007fffe1028541: mov 0x10(%rcx),%rax // 從_tags數(shù)組獲取操作數(shù)類型并存儲到%edx中 0x00007fffe1028545: movzbl 0x4(%rax,%rbx,1),%edx // $0x64代表JVM_CONSTANT_UnresolvedClass,比較,如果類還沒有鏈接, // 則直接跳轉(zhuǎn)到call_ldc 0x00007fffe102854a: cmp $0x64,%edx 0x00007fffe102854d: je 0x00007fffe102855d // call_ldc // $0x67代表JVM_CONSTANT_UnresolvedClassInError,也就是如果類在 // 鏈接過程中出現(xiàn)錯誤,則跳轉(zhuǎn)到call_ldc 0x00007fffe102854f: cmp $0x67,%edx 0x00007fffe1028552: je 0x00007fffe102855d // call_ldc // $0x7代表JVM_CONSTANT_Class,表示如果類已經(jīng)進行了連接,則 // 跳轉(zhuǎn)到notClass 0x00007fffe1028554: cmp $0x7,%edx 0x00007fffe1028557: jne 0x00007fffe10287c0 // notClass // 類在沒有連接或連接過程中出錯,則執(zhí)行如下的匯編代碼 // -- call_ldc --
下面看一下調(diào)用call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), c_rarg1
)函數(shù)生成的匯編代碼,CAST_FROM_FN_PTR
是宏,宏擴展后為( (address)((address_word)(InterpreterRuntime::ldc)) )。
在調(diào)用call_VM()函數(shù)時,傳遞的參數(shù)如下:
%rax
現(xiàn)在存儲類型數(shù)組首地址,不過傳入是為了接收調(diào)用函數(shù)的結(jié)果值adr是InterpreterRuntime::ldc()
函數(shù)首地址c_rarg1
用rdi寄存器存儲wide值,這里為0,表示為沒有加wide前綴的ldc指令生成匯編代碼
生成的匯編代碼如下:
第2部分:
// 將wide的值移到%esi寄存器,為后續(xù) // 調(diào)用InterpreterRuntime::ldc()函數(shù)準(zhǔn)備第2個參數(shù) 0x00007fffe102855d: mov $0x0,%esi // 調(diào)用MacroAssembler::call_VM()函數(shù),通過此函數(shù)來調(diào)用HotSpot VM中用 // C++編寫的函數(shù),通過這個C++編寫的函數(shù)來調(diào)用InterpreterRuntime::ldc()函數(shù) 0x00007fffe1017542: callq 0x00007fffe101754c 0x00007fffe1017547: jmpq 0x00007fffe10175df // 跳轉(zhuǎn)到E1 // 調(diào)用MacroAssembler::call_VM_helper()函數(shù) // 將棧頂存儲的返回地址設(shè)置到%rax中,也就是將存儲地址0x00007fffe1017547 // 的棧的slot地址設(shè)置到%rax中 0x00007fffe101754c: lea 0x8(%rsp),%rax // 調(diào)用InterpreterMacroAssembler::call_VM_base()函數(shù) // 存儲bcp到棧中特定位置 0x00007fffe1017551: mov %r13,-0x38(%rbp) // 調(diào)用MacroAssembler::call_VM_base()函數(shù) // 將r15中的值移動到rdi寄存器中,也就是為函數(shù)調(diào)用準(zhǔn)備第一個參數(shù) 0x00007fffe1017555: mov %r15,%rdi // 只有解釋器才必須要設(shè)置fp // 將last_java_fp保存到JavaThread類的last_java_fp屬性中 0x00007fffe1017558: mov %rbp,0x200(%r15) // 將last_java_sp保存到JavaThread類的last_java_sp屬性中 0x00007fffe101755f: mov %rax,0x1f0(%r15) // ... 省略調(diào)用MacroAssembler::call_VM_leaf_base()函數(shù) // 重置JavaThread::last_java_sp與JavaThread::last_java_fp屬性的值 0x00007fffe1017589: movabs $0x0,%r10 0x00007fffe1017593: mov %r10,0x1f0(%r15) 0x00007fffe101759a: movabs $0x0,%r10 0x00007fffe10175a4: mov %r10,0x200(%r15) // check for pending exceptions (java_thread is set upon return) 0x00007fffe10175ab: cmpq $0x0,0x8(%r15) // 如果沒有異常則直接跳轉(zhuǎn)到ok 0x00007fffe10175b3: je 0x00007fffe10175be // 如果有異常則跳轉(zhuǎn)到StubRoutines::forward_exception_entry()獲取的例程入口 0x00007fffe10175b9: jmpq 0x00007fffe1000420 // -- ok -- // 將JavaThread::vm_result屬性中的值存儲到%rax寄存器中并清空vm_result屬性的值 0x00007fffe10175be: mov 0x250(%r15),%rax 0x00007fffe10175c5: movabs $0x0,%r10 0x00007fffe10175cf: mov %r10,0x250(%r15) // 結(jié)束調(diào)用MacroAssembler::call_VM_base()函數(shù) // 恢復(fù)bcp與locals 0x00007fffe10175d6: mov -0x38(%rbp),%r13 0x00007fffe10175da: mov -0x30(%rbp),%r14 // 結(jié)束調(diào)用MacroAssembler::call_VM_helper()函數(shù) 0x00007fffe10175de: retq // 結(jié)束調(diào)用MacroAssembler::call_VM()函數(shù)
下面詳細解釋如下匯編的意思?! ?br />
call指令相當(dāng)于如下兩條指令:
push %eip
jmp addr
而ret指令相當(dāng)于:
pop %eip
所以如上匯編代碼:
0x00007fffe1017542: callq 0x00007fffe101754c 0x00007fffe1017547: jmpq 0x00007fffe10175df // 跳轉(zhuǎn) ... 0x00007fffe10175de: retq
調(diào)用callq
指令將jmpq
的地址壓入了表達式棧,也就是壓入了返回地址x00007fffe1017547
,這樣當(dāng)后續(xù)調(diào)用retq
時,會跳轉(zhuǎn)到jmpq
指令執(zhí)行,而jmpq
又跳轉(zhuǎn)到了0x00007fffe10175df
地址處的指令執(zhí)行。
通過調(diào)用MacroAssembler::call_VM()
函數(shù)來調(diào)用HotSpot VM
中用的C++編寫的函數(shù),call_VM()
函數(shù)還會調(diào)用如下函數(shù):
MacroAssembler::call_VM_helper InterpreterMacroAssembler::call_VM_base() MacroAssembler::call_VM_base() MacroAssembler::call_VM_leaf_base()
在如上幾個函數(shù)中,最重要的就是在MacroAssembler::call_VM_base()
函數(shù)中保存rsp、rbp的值到JavaThread::last_java_sp
與JavaThread::last_java_fp
屬性中,然后通過MacroAssembler::call_VM_leaf_base()
函數(shù)生成的匯編代碼來調(diào)用C++編寫的InterpreterRuntime::ldc()
函數(shù),如果調(diào)用InterpreterRuntime::ldc()
函數(shù)有可能破壞rsp和rbp的值(其它的%r13、%r14等的寄存器中的值也有可能破壞,所以在必要時保存到棧中,在調(diào)用完成后再恢復(fù),這樣這些寄存器其實就算的上是調(diào)用者保存的寄存器了),所以為了保證rsp、rbp,將這兩個值存儲到線程中,在線程中保存的這2個值對于棧展開非常非常重要,后面我們會詳細介紹。
由于如上匯編代碼會解釋執(zhí)行,在解釋執(zhí)行過程中會調(diào)用C++函數(shù),所以C/C++棧
和Java棧都混在一起,這為我們查找?guī)砹艘欢ǖ膹?fù)雜度。
調(diào)用的MacroAssembler::call_VM_leaf_base()
函數(shù)生成的匯編代碼如下:
第3部分匯編代碼:
// 調(diào)用MacroAssembler::call_VM_leaf_base()函數(shù) 0x00007fffe1017566: test $0xf,%esp // 檢查對齊 // %esp對齊的操作,跳轉(zhuǎn)到 L 0x00007fffe101756c: je 0x00007fffe1017584 // %esp沒有對齊時的操作 0x00007fffe1017572: sub $0x8,%rsp 0x00007fffe1017576: callq 0x00007ffff66a22a2 // 調(diào)用函數(shù),也就是調(diào)用InterpreterRuntime::ldc()函數(shù) 0x00007fffe101757b: add $0x8,%rsp 0x00007fffe101757f: jmpq 0x00007fffe1017589 // 跳轉(zhuǎn)到E2 // -- L -- // %esp對齊的操作 0x00007fffe1017584: callq 0x00007ffff66a22a2 // 調(diào)用函數(shù),也就是調(diào)用InterpreterRuntime::ldc()函數(shù) // -- E2 -- // 結(jié)束調(diào)用 MacroAssembler::call_VM_leaf_base()函數(shù)
在如上這段匯編中會真正調(diào)用C++函數(shù)InterpreterRuntime::ldc(),
由于這是一個C++函數(shù),所以在調(diào)用時,如果要傳遞參數(shù),則要遵守C++調(diào)用約定,也就是前6個參數(shù)都放到固定的寄存器中。這個函數(shù)需要2個參數(shù),分別為thread
和wide
,已經(jīng)分別放到了%rdi和%rax寄存器中了。InterpreterRuntime::ldc()
函數(shù)的實現(xiàn)如下:
// ldc負責(zé)將數(shù)值常量或String常量值從常量池中推送到棧頂 IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide)) ConstantPool* pool = method(thread)->constants(); int index = wide ? get_index_u2(thread, Bytecodes::_ldc_w) : get_index_u1(thread, Bytecodes::_ldc); constantTag tag = pool->tag_at(index); Klass* klass = pool->klass_at(index, CHECK); oop java_class = klass->java_mirror(); // java.lang.Class通過oop來表示 thread->set_vm_result(java_class); IRT_END
函數(shù)將查找到的、當(dāng)前正在解釋執(zhí)行的方法所屬的類存儲到JavaThread
類的vm_result
屬性中。我們可以回看第2部分匯編代碼,會將vm_result
屬性的值設(shè)置到%rax中。
接下來繼續(xù)看TemplateTable::ldc(bool wide)
函數(shù)生成的匯編代碼,此時已經(jīng)通過調(diào)用call_VM()函數(shù)生成了調(diào)用InterpreterRuntime::ldc()
這個C++的匯編,調(diào)用完成后值已經(jīng)放到了%rax
中。
// -- E1 -- 0x00007fffe10287ba: push %rax // 將調(diào)用的結(jié)果存儲到表達式中 0x00007fffe10287bb: jmpq 0x00007fffe102885e // 跳轉(zhuǎn)到Done // -- notClass -- // $0x4表示JVM_CONSTANT_Float 0x00007fffe10287c0: cmp $0x4,%edx 0x00007fffe10287c3: jne 0x00007fffe10287d9 // 跳到notFloat // 當(dāng)ldc字節(jié)碼指令加載的數(shù)為float時執(zhí)行如下匯編代碼 0x00007fffe10287c5: vmovss 0x58(%rcx,%rbx,8),%xmm0 0x00007fffe10287cb: sub $0x8,%rsp 0x00007fffe10287cf: vmovss %xmm0,(%rsp) 0x00007fffe10287d4: jmpq 0x00007fffe102885e // 跳轉(zhuǎn)到Done // -- notFloat -- // 當(dāng)ldc字節(jié)碼指令加載的為非float,也就是int類型數(shù)據(jù)時通過push加入表達式棧 0x00007fffe1028859: mov 0x58(%rcx,%rbx,8),%eax 0x00007fffe102885d: push %rax // -- Done --
由于ldc指令除了加載String
外,還可能加載int
和float
,如果是int,直接調(diào)用push壓入表達式棧中,如果是float,則在表達式棧上開辟空間,然后移到到這個開辟的slot
中存儲。注意,float會使用%xmm0
寄存器。
2、fast_aldc虛擬機內(nèi)部字節(jié)碼指令
下面介紹_fast_aldc
指令,這個指令是虛擬機內(nèi)部使用的指令而非虛擬機規(guī)范定義的指令。_fast_aldc
指令的模板定義如下:
def(Bytecodes::_fast_aldc , ubcp|____|clvm|____, vtos, atos, fast_aldc , false );
生成函數(shù)為TemplateTable::fast_aldc(bool wide),
這個函數(shù)生成的匯編代碼如下:
// 調(diào)用InterpreterMacroAssembler::get_cache_index_at_bcp()函數(shù)生成 // 獲取字節(jié)碼指令的操作數(shù),這個操作數(shù)已經(jīng)指向了常量池緩存項的索引,在字節(jié)碼重寫 // 階段已經(jīng)進行了字節(jié)碼重寫 0x00007fffe10243d0: movzbl 0x1(%r13),%edx // 調(diào)用InterpreterMacroAssembler::load_resolved_reference_at_index()函數(shù)生成 // shl表示邏輯左移,相當(dāng)于乘4,因為ConstantPoolCacheEntry的大小為4個字 0x00007fffe10243d5: shl $0x2,%edx // 獲取Method* 0x00007fffe10243d8: mov -0x18(%rbp),%rax // 獲取ConstMethod* 0x00007fffe10243dc: mov 0x10(%rax),%rax // 獲取ConstantPool* 0x00007fffe10243e0: mov 0x8(%rax),%rax // 獲取ConstantPool::_resolved_references屬性的值,這個值 // 是一個指向?qū)ο髷?shù)組的指針 0x00007fffe10243e4: mov 0x30(%rax),%rax // JNIHandles::resolve(obj) 0x00007fffe10243e8: mov (%rax),%rax // 從_resolved_references數(shù)組指定的下標(biāo)索引處獲取oop,先進行索引偏移 0x00007fffe10243eb: add %rdx,%rax // 要在%rax上加0x10,是因為數(shù)組對象的頭大小為2個字,加上后 // %rax就指向了oop 0x00007fffe10243ee: mov 0x10(%rax),%eax
獲取_resolved_references
屬性的值,涉及到的2個屬性在ConstantPool
類中的定義如下:
// Array of resolved objects from the constant pool and map from resolved // object index to original constant pool index jobject _resolved_references; // jobject是指針類型 Array<u2>* _reference_map;
關(guān)于_resolved_references
指向的其實是Object
數(shù)組。在ConstantPool::initialize_resolved_references()
函數(shù)中初始化這個屬性。調(diào)用鏈如下:
ConstantPool::initialize_resolved_references() constantPool.cpp Rewriter::make_constant_pool_cache() rewriter.cpp Rewriter::Rewriter() rewriter.cpp Rewriter::rewrite() rewriter.cpp InstanceKlass::rewrite_class() instanceKlass.cpp InstanceKlass::link_class_impl() instanceKlass.cpp
后續(xù)如果需要連接ldc等指令時,可能會調(diào)用如下函數(shù):(我們只討論ldc加載String類型數(shù)據(jù)的問題,所以我們只看往_resolved_references
屬性中放入表示String的oop的邏輯,MethodType
與MethodHandle
將不再介紹,有興趣的可自行研究)
oop ConstantPool::string_at_impl( constantPoolHandle this_oop, int which, int obj_index, TRAPS ) { 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); return str; } void string_at_put(int which, int obj_index, oop str) { // 獲取類型為jobject的_resolved_references屬性的值 objArrayOop tmp = resolved_references(); tmp->obj_at_put(obj_index, str); }
在如上函數(shù)中向_resolved_references
數(shù)組中設(shè)置緩存的值。
大概的思路就是:如果ldc加載的是字符串,那么盡量通過_resolved_references
數(shù)組中一次性找到表示字符串的oop,否則要通過原常量池下標(biāo)索引找到Symbol
實例(Symbol實例是HotSpot VM
內(nèi)部使用的、用來表示字符串),根據(jù)Symbol實例生成對應(yīng)的oop,然后通過常量池緩存下標(biāo)索引設(shè)置到_resolved_references
中。當(dāng)下次查找時,通過這個常量池緩存下標(biāo)緩存找到表示字符串的oop
。
獲取到_resolved_references
屬性的值后接著看生成的匯編代碼,如下:
// ... // %eax中存儲著表示字符串的oop 0x00007fffe1024479: test %eax,%eax // 如果已經(jīng)獲取到了oop,則跳轉(zhuǎn)到resolved 0x00007fffe102447b: jne 0x00007fffe1024481 // 沒有獲取到oop,需要進行連接操作,0xe5是_fast_aldc的Opcode 0x00007fffe1024481: mov $0xe5,%edx
調(diào)用call_VM()
函數(shù)生成的匯編代碼如下:
// 調(diào)用InterpreterRuntime::resolve_ldc()函數(shù) 0x00007fffe1024486: callq 0x00007fffe1024490 0x00007fffe102448b: jmpq 0x00007fffe1024526 // 將%rdx中的ConstantPoolCacheEntry項存儲到第1個參數(shù)中 // 調(diào)用MacroAssembler::call_VM_helper()函數(shù)生成 0x00007fffe1024490: mov %rdx,%rsi // 將返回地址加載到%rax中 0x00007fffe1024493: lea 0x8(%rsp),%rax // 調(diào)用call_VM_base()函數(shù)生成 // 保存bcp 0x00007fffe1024498: mov %r13,-0x38(%rbp) // 調(diào)用MacroAssembler::call_VM_base()函數(shù)生成 // 將r15中的值移動到c_rarg0(rdi)寄存器中,也就是為函數(shù)調(diào)用準(zhǔn)備第一個參數(shù) 0x00007fffe102449c: mov %r15,%rdi // Only interpreter should have to set fp 只有解釋器才必須要設(shè)置fp 0x00007fffe102449f: mov %rbp,0x200(%r15) 0x00007fffe10244a6: mov %rax,0x1f0(%r15) // 調(diào)用MacroAssembler::call_VM_leaf_base()生成 0x00007fffe10244ad: test $0xf,%esp 0x00007fffe10244b3: je 0x00007fffe10244cb 0x00007fffe10244b9: sub $0x8,%rsp 0x00007fffe10244bd: callq 0x00007ffff66b27ac 0x00007fffe10244c2: add $0x8,%rsp 0x00007fffe10244c6: jmpq 0x00007fffe10244d0 0x00007fffe10244cb: callq 0x00007ffff66b27ac 0x00007fffe10244d0: movabs $0x0,%r10 // 結(jié)束調(diào)用MacroAssembler::call_VM_leaf_base() 0x00007fffe10244da: mov %r10,0x1f0(%r15) 0x00007fffe10244e1: movabs $0x0,%r10 // 檢查是否有異常發(fā)生 0x00007fffe10244eb: mov %r10,0x200(%r15) 0x00007fffe10244f2: cmpq $0x0,0x8(%r15) // 如果沒有異常發(fā)生,則跳轉(zhuǎn)到ok 0x00007fffe10244fa: je 0x00007fffe1024505 // 有異常發(fā)生,則跳轉(zhuǎn)到StubRoutines::forward_exception_entry() 0x00007fffe1024500: jmpq 0x00007fffe1000420 // ---- ok ---- // 將JavaThread::vm_result屬性中的值存儲到oop_result寄存器中并清空vm_result屬性的值 0x00007fffe1024505: mov 0x250(%r15),%rax 0x00007fffe102450c: movabs $0x0,%r10 0x00007fffe1024516: mov %r10,0x250(%r15) // 結(jié)果調(diào)用MacroAssembler::call_VM_base()函數(shù) // 恢復(fù)bcp和locals 0x00007fffe102451d: mov -0x38(%rbp),%r13 0x00007fffe1024521: mov -0x30(%rbp),%r14 // 結(jié)束調(diào)用InterpreterMacroAssembler::call_VM_base()函數(shù) // 結(jié)束調(diào)用MacroAssembler::call_VM_helper()函數(shù) 0x00007fffe1024525: retq // 結(jié)束調(diào)用MacroAssembler::call_VM()函數(shù),回到 // TemplateTable::fast_aldc()函數(shù)繼續(xù)看生成的代碼,只 // 定義了resolved點 // ---- resolved ----
調(diào)用的InterpreterRuntime::resolve_ldc()
函數(shù)的實現(xiàn)如下:
IRT_ENTRY(void, InterpreterRuntime::resolve_ldc( JavaThread* thread, Bytecodes::Code bytecode) ) { ResourceMark rm(thread); methodHandle m (thread, method(thread)); Bytecode_loadconstant ldc(m, bci(thread)); oop result = ldc.resolve_constant(CHECK); thread->set_vm_result(result); } IRT_END
這個函數(shù)會調(diào)用一系列的函數(shù),相關(guān)調(diào)用鏈如下:
ConstantPool::string_at_put() constantPool.hpp ConstantPool::string_at_impl() constantPool.cpp ConstantPool::resolve_constant_at_impl() constantPool.cpp ConstantPool::resolve_cached_constant_at() constantPool.hpp Bytecode_loadconstant::resolve_constant() bytecode.cpp InterpreterRuntime::resolve_ldc() interpreterRuntime.cpp
調(diào)用的resolve_constant()
函數(shù)的實現(xiàn)如下:
oop Bytecode_loadconstant::resolve_constant(TRAPS) const { 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); } }
調(diào)用的resolve_cached_constant_at()
或resolve_constant_at()
函數(shù)的實現(xiàn)如下:
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); } oop resolve_possibly_cached_constant_at(int pool_index, TRAPS) { constantPoolHandle h_this(THREAD, this); return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, THREAD); }
調(diào)用的resolve_constant_at_impl()
函數(shù)的實現(xiàn)如下:
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) { cache_index = this_oop->cp_to_object_index(index); } if (cache_index >= 0) { result_oop = this_oop->resolved_references()->obj_at(cache_index); if (result_oop != NULL) { return result_oop; } 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_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; // ... } if (cache_index >= 0) { Handle result_handle(THREAD, result_oop); MonitorLockerEx ml(this_oop->lock()); oop result = this_oop->resolved_references()->obj_at(cache_index); if (result == NULL) { this_oop->resolved_references()->obj_at_put(cache_index, result_handle()); return result_handle(); } else { return result; } } else { return result_oop; } }
通過常量池的tags
數(shù)組判斷,如果常量池下標(biāo)index
處存儲的是JVM_CONSTANT_String
常量池項,則調(diào)用string_at_impl()
函數(shù),這個函數(shù)在之前已經(jīng)介紹過,會根據(jù)表示字符串的Symbol實例創(chuàng)建出表示字符串的oop。在ConstantPool::resolve_constant_at_impl()
函數(shù)中得到oop后就存儲到ConstantPool::_resolved_references
屬性中,最后返回這個oop,這正是ldc需要的oop?!?/p>
通過重寫fast_aldc字節(jié)碼指令,達到了通過少量指令就直接獲取到oop的目的,而且oop是緩存的,所以字符串常量在HotSpot VM中的表示唯一,也就是只有一個oop表示。
C++函數(shù)約定返回的值會存儲到%rax
中,根據(jù)_fast_aldc字節(jié)碼指令的模板定義可知,tos_out
為atos,
所以后續(xù)并不需要進一步操作。
到此這篇關(guān)于Java加載與存儲指令之ldc與_fast_aldc指令的文章就介紹到這了,更多相關(guān)Java的ldc與_fast_aldc指令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring多線程通過@Scheduled實現(xiàn)定時任務(wù)
這篇文章主要介紹了Spring多線程通過@Scheduled實現(xiàn)定時任務(wù),@Scheduled?定時任務(wù)調(diào)度注解,是spring定時任務(wù)中最重要的,下文關(guān)于其具體介紹,需要的小伙伴可以參考一下2022-05-05Java數(shù)據(jù)結(jié)構(gòu)與算法之單鏈表深入理解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法之單鏈表深入理解,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09Spring Boot2開發(fā)之Spring Boot整合Shiro兩種詳細方法
這篇文章主要介紹了Spring Boot2開發(fā)之Spring Boot整合Shiro詳細方法,需要的朋友可以參考下2020-03-03Spring使用@Autowired注解靜態(tài)實例對象方式
這篇文章主要介紹了Spring使用@Autowired注解靜態(tài)實例對象方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08