欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java加載與存儲指令之ldc與_fast_aldc指令

 更新時間:2021年09月14日 10:37:04   作者:歸去來兮  
ldc指令將int、float、或者一個類、方法類型或方法句柄的符號引用、還可能是String型常量值從常量池中推送至棧頂。這一篇介紹一個虛擬機規(guī)范中定義的一個字節(jié)碼指令ldc,另外還有一個虛擬機內(nèi)部使用的字節(jié)碼指令_fast_aldc。需要的盆友可參考下面文章的內(nèi)容

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_spJavaThread::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ù),分別為threadwide,已經(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外,還可能加載intfloat,如果是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的邏輯,MethodTypeMethodHandle將不再介紹,有興趣的可自行研究)

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_outatos,所以后續(xù)并不需要進一步操作。

到此這篇關(guān)于Java加載與存儲指令之ldc與_fast_aldc指令的文章就介紹到這了,更多相關(guān)Java的ldc與_fast_aldc指令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論