触发OSR 编译(以goto指令为例)及安装与卸载
流程圖如下。
決定一個方法是否為熱點代碼的因素有兩個:方法的調(diào)用次數(shù)、循環(huán)回邊的執(zhí)行次數(shù)。即時編譯便是根據(jù)這兩個計數(shù)器的和來觸發(fā)的。為什么 Java 虛擬機需要維護兩個不同的計數(shù)器呢?
實際上,除了以方法為單位的即時編譯之外,Java 虛擬機還存在著另一種以循環(huán)為單位的即時編譯,叫做 On-Stack-Replacement(OSR)編譯。循環(huán)回邊計數(shù)器便是用來觸發(fā)這種類型的編譯的。
OSR 實際上是一種技術(shù),它指的是在程序執(zhí)行過程中,動態(tài)地替換掉 Java 方法棧楨,從而使得程序能夠在非方法入口處進行解釋執(zhí)行和編譯后的代碼之間的切換。事實上,去優(yōu)化(deoptimization)采用的技術(shù)也可以稱之為 OSR。
在不啟用分層編譯的情況下,觸發(fā) OSR 編譯的閾值是由參數(shù) -XX:CompileThreshold 指定的閾值的倍數(shù)。
該倍數(shù)的計算方法為:
(OnStackReplacePercentage - InterpreterProfilePercentage)/100
其中 -XX:InterpreterProfilePercentage 的默認值為 33,當(dāng)使用 C1 時 -XX:OnStackReplacePercentage 為 933,當(dāng)使用 C2 時為 140。
也就是說,默認情況下,C1 的 OSR 編譯的閾值為 13500,而 C2 的為 10700。
在啟用分層編譯的情況下,觸發(fā) OSR 編譯的閾值則是由參數(shù) -XX:TierXBackEdgeThreshold 指定的閾值乘以系數(shù)。
OSR 編譯在正常的應(yīng)用程序中并不多見。它只在基準測試時比較常見,因此并不需要過多了解。
通過調(diào)用TemplateTable::_goto()函數(shù)來完成goto()指令的機器代碼生成。如下:
// 將當(dāng)前棧幀中保存的Method* 拷貝到rcx中 0x00007fffe101dd10: mov -0x18(%rbp),%rcx // 如果開啟了profile則執(zhí)行分支跳轉(zhuǎn)相關(guān)的性能統(tǒng)計 // If no method data exists, go to profile_continue. // Otherwise, assign to mdp 0x00007fffe101dd14: mov -0x20(%rbp),%rax 0x00007fffe101dd18: test %rax,%rax 0x00007fffe101dd1b: je 0x00007fffe101dd39 // 將JumpData::taken_off_set存儲到%rax中 0x00007fffe101dd21: mov 0x8(%rax),%rbx // 增加DataLayout::counter_increment,值為1 0x00007fffe101dd25: add $0x1,%rbx // sbb是帶借位減法指令 0x00007fffe101dd29: sbb $0x0,%rbx // 存儲回JumpData::taken_off_set中 0x00007fffe101dd2d: mov %rbx,0x8(%rax) // The method data pointer needs to be updated to reflect the new target. // 將JumpData::displacement_off_set // %rax中存儲的是MethodData* 0x00007fffe101dd31: add 0x10(%rax),%rax // 將計算出的值存儲到棧中interpreter_frame_mdx_offset偏向處 0x00007fffe101dd35: mov %rax,-0x20(%rbp) // **** profile_continue **** // 將當(dāng)前字節(jié)碼位置往后偏移1字節(jié)處開始的2字節(jié)數(shù)據(jù)讀取到rdx中 0x00007fffe101dd39: movswl 0x1(%r13),%edx // 將rdx中的值字節(jié)次序變反 0x00007fffe101dd3e: bswap %edx // 將rdx中的值右移16位,上述兩步就是為了計算跳轉(zhuǎn)分支的偏移量 0x00007fffe101dd40: sar $0x10,%edx // 將rdx中的數(shù)據(jù)從2字節(jié)擴展成4字節(jié) 0x00007fffe101dd43: movslq %edx,%rdx // 將當(dāng)前字節(jié)碼地址加上rdx保存的偏移量,計算跳轉(zhuǎn)的目標地址 0x00007fffe101dd46: add %rdx,%r13
如果UseLoopCounter為true時才會有如下匯編,在執(zhí)行如下匯編時,各個寄存器的狀態(tài)如下:
increment backedge counter for backward branches
rax: MDO ebx: MDO bumped taken-count rcx: method rdx: target offset r13: target bcp r14: locals pointer
匯編如下:
// 校驗rdx是否大于0,如果大于0說明是往前跳轉(zhuǎn),如果小于0說明是往后跳轉(zhuǎn),如果大于0則跳轉(zhuǎn)到dispatch,這樣只有回邊才會進行統(tǒng)計 0x00007fffe101dd49: test %edx,%edx 0x00007fffe101dd4b: jns 0x00007fffe101de30 // 跳轉(zhuǎn)到dispatch // 執(zhí)行這里時,說明有回邊需要統(tǒng)計 // 檢查Method::_method_counters是否為NULL,如果非空則跳轉(zhuǎn)到has_counters 0x00007fffe101dd51: mov 0x20(%rcx),%rax 0x00007fffe101dd55: test %rax,%rax 0x00007fffe101dd58: jne 0x00007fffe101ddf4 // 如果為空,則通過InterpreterRuntime::build_method_counters()函數(shù)創(chuàng)建一個新的MethodCounters 0x00007fffe101dd5e: push %rdx 0x00007fffe101dd5f: push %rcx 0x00007fffe101dd60: callq 0x00007fffe101dd6a 0x00007fffe101dd65: jmpq 0x00007fffe101dde8 0x00007fffe101dd6a: mov %rcx,%rsi 0x00007fffe101dd6d: lea 0x8(%rsp),%rax 0x00007fffe101dd72: mov %r13,-0x38(%rbp) 0x00007fffe101dd76: mov %r15,%rdi 0x00007fffe101dd79: mov %rbp,0x200(%r15) 0x00007fffe101dd80: mov %rax,0x1f0(%r15) 0x00007fffe101dd87: test $0xf,%esp 0x00007fffe101dd8d: je 0x00007fffe101dda5 0x00007fffe101dd93: sub $0x8,%rsp 0x00007fffe101dd97: callq 0x00007ffff66b581c 0x00007fffe101dd9c: add $0x8,%rsp 0x00007fffe101dda0: jmpq 0x00007fffe101ddaa 0x00007fffe101dda5: callq 0x00007ffff66b581c 0x00007fffe101ddaa: movabs $0x0,%r10 0x00007fffe101ddb4: mov %r10,0x1f0(%r15) 0x00007fffe101ddbb: movabs $0x0,%r10 0x00007fffe101ddc5: mov %r10,0x200(%r15) 0x00007fffe101ddcc: cmpq $0x0,0x8(%r15) 0x00007fffe101ddd4: je 0x00007fffe101dddf 0x00007fffe101ddda: jmpq 0x00007fffe1000420 0x00007fffe101dddf: mov -0x38(%rbp),%r13 0x00007fffe101dde3: mov -0x30(%rbp),%r14 0x00007fffe101dde7: retq 0x00007fffe101dde8: pop %rcx 0x00007fffe101dde9: pop %rdx // 將創(chuàng)建出新的MethodCounters存儲到%rax中 0x00007fffe101ddea: mov 0x20(%rcx),%rax //如果創(chuàng)建失敗,則跳轉(zhuǎn)到到dispatch分支 0x00007fffe101ddee: je 0x00007fffe101de30 // ....
如下在啟動分層編譯時才會生成的匯編:
// **** has_counters **** // 開啟profile性能收集才會生成的匯編 // 獲取Method::_method_data屬性到rbx中,并校驗其是否為空,如果為空則跳轉(zhuǎn)到no_mdo 0x00007fffe101ddf4: mov 0x18(%rcx),%rbx 0x00007fffe101ddf8: test %rbx,%rbx 0x00007fffe101ddfb: je 0x00007fffe101de17 //Method::_method_data屬性不為空,則增加Method::_method_data::_backedge_counter計數(shù)值,如果超過閾值則跳轉(zhuǎn)到backedge_counter_overflow 0x00007fffe101ddfd: mov 0x70(%rbx),%eax 0x00007fffe101de00: add $0x8,%eax 0x00007fffe101de03: mov %eax,0x70(%rbx) 0x00007fffe101de06: and $0x1ff8,%eax 0x00007fffe101de0c: je 0x00007fffe101df22 // 跳轉(zhuǎn)到backedge_counter_overflow // 跳轉(zhuǎn)到dispatch 0x00007fffe101de12: jmpq 0x00007fffe101de30 // **** no_mdo **** // 增加Method::_method_counters::backedge_counter的調(diào)用計數(shù),如果超過閾值則跳轉(zhuǎn)到backedge_counter_overflow 0x00007fffe101de17: mov 0x20(%rcx),%rcx 0x00007fffe101de1b: mov 0xc(%rcx),%eax 0x00007fffe101de1e: add $0x8,%eax 0x00007fffe101de21: mov %eax,0xc(%rcx) 0x00007fffe101de24: and $0x1ff8,%eax 0x00007fffe101de2a: je 0x00007fffe101df22 // 跳轉(zhuǎn)到backedge_counter_overflow // **** dispatch **** // r13已經(jīng)變成目標跳轉(zhuǎn)地址,這里是加載跳轉(zhuǎn)地址的第一個字節(jié)碼到rbx中 0x00007fffe101de30: movzbl 0x0(%r13),%ebx 0x00007fffe101de35: movabs $0x7ffff73b9e40,%r10 0x00007fffe101de3f: jmpq *(%r10,%rbx,8) // **** profile_method **** // 通過call_VM()函數(shù)來調(diào)用InterpreterRuntime::profile_method()函數(shù) 0x00007fffe101de43: callq 0x00007fffe101de4d 0x00007fffe101de48: jmpq 0x00007fffe101dec8 0x00007fffe101de4d: lea 0x8(%rsp),%rax 0x00007fffe101de52: mov %r13,-0x38(%rbp) 0x00007fffe101de56: mov %r15,%rdi 0x00007fffe101de59: mov %rbp,0x200(%r15) 0x00007fffe101de60: mov %rax,0x1f0(%r15) 0x00007fffe101de67: test $0xf,%esp 0x00007fffe101de6d: je 0x00007fffe101de85 0x00007fffe101de73: sub $0x8,%rsp 0x00007fffe101de77: callq 0x00007ffff66b4d84 0x00007fffe101de7c: add $0x8,%rsp 0x00007fffe101de80: jmpq 0x00007fffe101de8a 0x00007fffe101de85: callq 0x00007ffff66b4d84 0x00007fffe101de8a: movabs $0x0,%r10 0x00007fffe101de94: mov %r10,0x1f0(%r15) 0x00007fffe101de9b: movabs $0x0,%r10 0x00007fffe101dea5: mov %r10,0x200(%r15) 0x00007fffe101deac: cmpq $0x0,0x8(%r15) 0x00007fffe101deb4: je 0x00007fffe101debf 0x00007fffe101deba: jmpq 0x00007fffe1000420 0x00007fffe101debf: mov -0x38(%rbp),%r13 0x00007fffe101dec3: mov -0x30(%rbp),%r14 0x00007fffe101dec7: retq // 結(jié)束call_VM()函數(shù)結(jié)束 // 調(diào)用set_method_data_pointer_for_bcp()函數(shù)生成的匯編 // restore target bytecode 0x00007fffe101dec8: movzbl 0x0(%r13),%ebx 0x00007fffe101decd: push %rax 0x00007fffe101dece: push %rbx // 獲取Method::_method_data并存儲到%rax中 0x00007fffe101decf: mov -0x18(%rbp),%rbx 0x00007fffe101ded3: mov 0x18(%rbx),%rax // 如果Method::_method_data為NULL,則跳轉(zhuǎn)到set_mdp 0x00007fffe101ded7: test %rax,%rax 0x00007fffe101deda: je 0x00007fffe101df17 // 通過call_VM_leaf()函數(shù)調(diào)用InterpreterRuntime::bcp_to_di()函數(shù) 0x00007fffe101dee0: mov %r13,%rsi 0x00007fffe101dee3: mov %rbx,%rdi 0x00007fffe101dee6: test $0xf,%esp 0x00007fffe101deec: je 0x00007fffe101df04 0x00007fffe101def2: sub $0x8,%rsp 0x00007fffe101def6: callq 0x00007ffff66b4bb4 0x00007fffe101defb: add $0x8,%rsp 0x00007fffe101deff: jmpq 0x00007fffe101df09 0x00007fffe101df04: callq 0x00007ffff66b4bb4 // rax: mdi // mdo is guaranteed to be non-zero here, we checked for it before the call. // 將Method::_method_data存儲到%rbx中 0x00007fffe101df09: mov 0x18(%rbx),%rbx // 增加Method::_method_data::_data偏移 0x00007fffe101df0d: add $0x90,%rbx 0x00007fffe101df14: add %rbx,%rax // **** set_mdp **** // 通過interpreter_frame_mdx_offset來獲取mdx 0x00007fffe101df17: mov %rax,-0x20(%rbp) 0x00007fffe101df1b: pop %rbx 0x00007fffe101df1c: pop %rax // 結(jié)束set_method_data_pointer_for_bcp()函數(shù)調(diào)用 0x00007fffe101df1d: jmpq 0x00007fffe101de30
調(diào)用的InterpreterRuntime::profile_method()函數(shù)的實現(xiàn)如下:
IRT_ENTRY(void, InterpreterRuntime::profile_method(JavaThread* thread))
// use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized
// flag, in case this method triggers classloading which will call into Java.
UnlockFlagSaver fs(thread);
assert(ProfileInterpreter, "must be profiling interpreter");
frame fr = thread->last_frame();
assert(fr.is_interpreted_frame(), "must come from interpreter");
methodHandle method(thread, fr.interpreter_frame_method());
Method::build_interpreter_method_data(method, THREAD);
IRT_END
// Build a MethodData* object to hold information about this method
// collected in the interpreter.
void Method::build_interpreter_method_data(methodHandle method, TRAPS) {
// Do not profile method if current thread holds the pending list lock,
// which avoids deadlock for acquiring the MethodData_lock.
if (InstanceRefKlass::owns_pending_list_lock((JavaThread*)THREAD)) {
return;
}
// Grab a lock here to prevent multiple MethodData*s from being created.
MutexLocker ml(MethodData_lock, THREAD);
if (method->method_data() == NULL) {
ClassLoaderData* loader_data = method->method_holder()->class_loader_data();
MethodData* method_data = MethodData::allocate(loader_data, method, CHECK);
method->set_method_data(method_data);
}
}
就是為Method::_method_data屬性創(chuàng)建MethodData對象并賦值?! ?/p>
// 只有開啟UseOnStackReplacement時才會生成如下匯編 // 當(dāng)超過閾值后會跳轉(zhuǎn)到此分支 // **** backedge_counter_overflow **** // 對rdx中的數(shù)取補碼 0x00007fffe101df22: neg %rdx // 將r13的地址加到rdx上,這兩步是計算跳轉(zhuǎn)地址 0x00007fffe101df25: add %r13,%rdx // 通過調(diào)用call_VM()函數(shù)來調(diào)用InterpreterRuntime::frequency_counter_overflow()函數(shù) 0x00007fffe101df28: callq 0x00007fffe101df32 0x00007fffe101df2d: jmpq 0x00007fffe101dfb0 0x00007fffe101df32: mov %rdx,%rsi 0x00007fffe101df35: lea 0x8(%rsp),%rax 0x00007fffe101df3a: mov %r13,-0x38(%rbp) 0x00007fffe101df3e: mov %r15,%rdi 0x00007fffe101df41: mov %rbp,0x200(%r15) 0x00007fffe101df48: mov %rax,0x1f0(%r15) 0x00007fffe101df4f: test $0xf,%esp 0x00007fffe101df55: je 0x00007fffe101df6d 0x00007fffe101df5b: sub $0x8,%rsp 0x00007fffe101df5f: callq 0x00007ffff66b45c8 0x00007fffe101df64: add $0x8,%rsp 0x00007fffe101df68: jmpq 0x00007fffe101df72 0x00007fffe101df6d: callq 0x00007ffff66b45c8 0x00007fffe101df72: movabs $0x0,%r10 0x00007fffe101df7c: mov %r10,0x1f0(%r15) 0x00007fffe101df83: movabs $0x0,%r10 0x00007fffe101df8d: mov %r10,0x200(%r15) 0x00007fffe101df94: cmpq $0x0,0x8(%r15) 0x00007fffe101df9c: je 0x00007fffe101dfa7 0x00007fffe101dfa2: jmpq 0x00007fffe1000420 0x00007fffe101dfa7: mov -0x38(%rbp),%r13 0x00007fffe101dfab: mov -0x30(%rbp),%r14 0x00007fffe101dfaf: retq // 結(jié)束call_VM()函數(shù)的調(diào)用 // 恢復(fù)待執(zhí)行的字節(jié)碼 0x00007fffe101dfb0: movzbl 0x0(%r13),%ebx // rax: osr nmethod (osr ok) or NULL (osr not possible) // ebx: target bytecode // rdx: scratch // r14: locals pointer // r13: bcp // 校驗frequency_counter_overflow()函數(shù)返回的編譯結(jié)果是否為空,如果為空則跳轉(zhuǎn)到dispatch,即繼續(xù)執(zhí)行字節(jié)碼 0x00007fffe101dfb5: test %rax,%rax 0x00007fffe101dfb8: je 0x00007fffe101de30 // 如果不為空,即表示方法編譯完成,將nmethod::_entry_bci屬性的偏移復(fù)制到rcx中 0x00007fffe101dfbe: mov 0x48(%rax),%ecx // 如果rcx等于InvalidOSREntryBci,則跳轉(zhuǎn)到dispatch 0x00007fffe101dfc1: cmp $0xfffffffe,%ecx 0x00007fffe101dfc4: je 0x00007fffe101de30 // 開始執(zhí)行棧上替換 // We have the address of an on stack replacement routine in eax // We need to prepare to execute the OSR method. First we must // migrate the locals and monitors off of the stack. 0x00007fffe101dfca: mov %rax,%r13 // save the nmethod // 通過調(diào)用call_VM()函數(shù)調(diào)用SharedRuntime::OSR_migration_begin()函數(shù) // // 調(diào)用OSR_migration_begin方法,完成棧幀上變量和monitor的遷移 0x00007fffe101dfcd: callq 0x00007fffe101dfd7 0x00007fffe101dfd2: jmpq 0x00007fffe101e052 0x00007fffe101dfd7: lea 0x8(%rsp),%rax 0x00007fffe101dfdc: mov %r13,-0x38(%rbp) 0x00007fffe101dfe0: mov %r15,%rdi 0x00007fffe101dfe3: mov %rbp,0x200(%r15) 0x00007fffe101dfea: mov %rax,0x1f0(%r15) 0x00007fffe101dff1: test $0xf,%esp 0x00007fffe101dff7: je 0x00007fffe101e00f 0x00007fffe101dffd: sub $0x8,%rsp 0x00007fffe101e001: callq 0x00007ffff6a18a6a 0x00007fffe101e006: add $0x8,%rsp 0x00007fffe101e00a: jmpq 0x00007fffe101e014 0x00007fffe101e00f: callq 0x00007ffff6a18a6a 0x00007fffe101e014: movabs $0x0,%r10 0x00007fffe101e01e: mov %r10,0x1f0(%r15) 0x00007fffe101e025: movabs $0x0,%r10 0x00007fffe101e02f: mov %r10,0x200(%r15) 0x00007fffe101e036: cmpq $0x0,0x8(%r15) 0x00007fffe101e03e: je 0x00007fffe101e049 0x00007fffe101e044: jmpq 0x00007fffe1000420 0x00007fffe101e049: mov -0x38(%rbp),%r13 0x00007fffe101e04d: mov -0x30(%rbp),%r14 0x00007fffe101e051: retq // eax is OSR buffer, move it to expected parameter location // 將rax中的值拷貝到%rsi(j_rarg0) 0x00007fffe101e052: mov %rax,%rsi // 獲取interpreter_frame_sender_sp_offset偏移處的值 0x00007fffe101e055: mov -0x8(%rbp),%rdx 0x00007fffe101e059: leaveq // remove frame anchor 0x00007fffe101e05a: pop %rcx // get return address 0x00007fffe101e05b: mov %rdx,%rsp // set sp to sender sp // Ensure compiled code always sees stack at proper alignment // -StackAlignmentInBytes為$0xfffffffffffffff0 0x00007fffe101e05e: and $0xfffffffffffffff0,%rsp // unlike x86 we need no specialized return from compiled code to the interpreter or the call stub. // push the return address 0x00007fffe101e062: push %rcx // 跳轉(zhuǎn)到nmethod::_osr_entry_point,開始執(zhí)行 0x00007fffe101e063: jmpq *0x88(%r13)
調(diào)用的SharedRuntime::OSR_migration_begin()函數(shù)的實現(xiàn)如下:
// OSR Migration Code
//
// This code is used convert interpreter frames into compiled frames. It is
// called from very start of a compiled OSR nmethod. A temp array is
// allocated to hold the interesting bits of the interpreter frame. All
// active locks are inflated to allow them to move. The displaced headers and
// active interpeter locals are copied into the temp buffer. Then we return
// back to the compiled code. The compiled code then pops the current
// interpreter frame off the stack and pushes a new compiled frame. Then it
// copies the interpreter locals and displaced headers where it wants.
// Finally it calls back to free the temp buffer.
//
// All of this is done NOT at any Safepoint, nor is any safepoint or GC allowed.
// 調(diào)用OSR_migration_begin方法,完成棧幀上變量和monitor的遷移
JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *thread) )
// This code is dependent on the memory layout of the interpreter local
// array and the monitors. On all of our platforms the layout is identical
// so this code is shared. If some platform lays the their arrays out
// differently then this code could move to platform specific code or
// the code here could be modified to copy items one at a time using
// frame accessor methods and be platform independent.
frame fr = thread->last_frame();
assert( fr.is_interpreted_frame(), "" );
assert( fr.interpreter_frame_expression_stack_size()==0, "only handle empty stacks" );
// Figure out how many monitors are active.
int active_monitor_count = 0;
for(
BasicObjectLock *kptr = fr.interpreter_frame_monitor_end();
kptr < fr.interpreter_frame_monitor_begin();
kptr = fr.next_monitor_in_interpreter_frame(kptr)
) {
if( kptr->obj() != NULL ) {
active_monitor_count++;
}
}
// QQQ we could place number of active monitors in the array so that compiled code
// could double check it.
Method* moop = fr.interpreter_frame_method();
int max_locals = moop->max_locals();
// Allocate temp buffer, 1 word per local & 2 per active monitor
int buf_size_words = max_locals + active_monitor_count*2;
intptr_t *buf = NEW_C_HEAP_ARRAY(intptr_t,buf_size_words, mtCode);
// Copy the locals. Order is preserved so that loading of longs works.
// Since there's no GC I can copy the oops blindly.
assert( sizeof(HeapWord)==sizeof(intptr_t), "fix this code");
Copy::disjoint_words((HeapWord*)fr.interpreter_frame_local_at(max_locals-1),
(HeapWord*)&buf[0],
max_locals);
// Inflate locks. Copy the displaced headers. Be careful, there can be holes.
int i = max_locals;
for(
BasicObjectLock *kptr2 = fr.interpreter_frame_monitor_end();
kptr2 < fr.interpreter_frame_monitor_begin();
kptr2 = fr.next_monitor_in_interpreter_frame(kptr2)
){
if( kptr2->obj() != NULL) { // Avoid 'holes' in the monitor array
BasicLock *lock = kptr2->lock();
// Inflate so the displaced header becomes position-independent,什么意思??
if (lock->displaced_header()->is_unlocked()){ // 無鎖狀態(tài)
ObjectSynchronizer::inflate_helper(kptr2->obj()); // 將鎖對象膨脹為重量級鎖
}
// Now the displaced header is free to move
buf[i++] = (intptr_t)lock->displaced_header();
buf[i++] = cast_from_oop<intptr_t>(kptr2->obj());
}
}
assert( i - max_locals == active_monitor_count*2, "found the expected number of monitors" );
return buf;
JRT_END
調(diào)用InstanceKlass::add_osr_nmethod()與InstanceKlass::remove_osr_nmethod()對OSR進行安裝與卸載。
add_osr_nmethod用于將需要執(zhí)行棧上替換的nmethod實例插入到InstanceKlass的osr_nmethods鏈表上,方法實現(xiàn)如下:
void InstanceKlass::add_osr_nmethod(nmethod* n) {
// 獲取鎖
OsrList_lock->lock_without_safepoint_check();
//校驗必須是棧上替換方法
assert(n->is_osr_method(), "wrong kind of nmethod");
// 將_osr_nmethods_head設(shè)置成n的下一個方法
n->set_osr_link(osr_nmethods_head());
// 將n設(shè)置為_osr_nmethods_head
set_osr_nmethods_head(n);
// 如果使用分層編譯
if (TieredCompilation) {
Method* m = n->method();
// 更新最高編譯級別
m->set_highest_osr_comp_level(MAX2(m->highest_osr_comp_level(), n->comp_level()));
}
// 解鎖
OsrList_lock->unlock();
// Get rid of the osr methods for the same bci that have lower levels.
if (TieredCompilation) {
// 查找所有低于nmethod的編譯級別的屬于同一方法的nmethod實例,將其從osr_nmethods鏈表上移除
for (int l = CompLevel_limited_profile; l < n->comp_level(); l++) {
nmethod *inv = lookup_osr_nmethod(n->method(), n->osr_entry_bci(), l, true);
if (inv != NULL && inv->is_in_use()) {
inv->make_not_entrant();
}
}
}
}
nmethod* osr_nmethods_head() const { return _osr_nmethods_head; };
與add_osr_nmethod相對應(yīng)的就是remove_osr_nmethod,用于從osr_nmethod鏈表上移除nmethod,當(dāng)nmethod被標記成not_entrant或者zombie時,或者執(zhí)行CodeCache垃圾回收時會調(diào)用該方法,其源碼說明如下:
void InstanceKlass::remove_osr_nmethod(nmethod* n) {
// 獲取鎖
OsrList_lock->lock_without_safepoint_check();
// 校驗是否棧上替換方法
assert(n->is_osr_method(), "wrong kind of nmethod");
nmethod* last = NULL;
// 獲取osr_nmethods鏈表的頭元素
nmethod* cur = osr_nmethods_head();
int max_level = CompLevel_none; // Find the max comp level excluding n
Method* m = n->method();
// 遍歷osr_nmethods鏈表直到遇到n,找到n所屬的方法的所有nmehtod的最高編譯級別
while(cur != NULL && cur != n) {
if (TieredCompilation && m == cur->method()) {
// Find max level before n
max_level = MAX2(max_level, cur->comp_level());
}
last = cur;
cur = cur->osr_link();
}
nmethod* next = NULL;
// 如果從鏈表中找到了目標nmethod
if (cur == n) {
// 將目標nmethod從鏈表中移除
next = cur->osr_link();
if (last == NULL) {
// Remove first element
set_osr_nmethods_head(next);
} else {
last->set_osr_link(next);
}
}
n->set_osr_link(NULL);
if (TieredCompilation) {
cur = next;
// 遍歷鏈表,更新最大編譯級別
while (cur != NULL) {
// Find max level after n
if (m == cur->method()) {
max_level = MAX2(max_level, cur->comp_level());
}
cur = cur->osr_link();
}
m->set_highest_osr_comp_level(max_level);
}
// Remember to unlock again
OsrList_lock->unlock();
}
參考文章:
(1)HotSpot中執(zhí)行引擎技術(shù)詳解(四)——hotspot探測
(2)下載The Java HotSpot Server Compiler論文
(3)Hotspot 熱點代碼編譯和棧上替換 源碼解析
(4)[討論][HotSpot VM] JIT編譯以及執(zhí)行native code的流程
總結(jié)
以上是生活随笔為你收集整理的触发OSR 编译(以goto指令为例)及安装与卸载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python爬虫入门 之 移动端数据的爬
- 下一篇: Relu激活函数的优点