自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java開源工具在linux上的源碼分析(四):safe point

開發(fā) 后端
safe point 顧明思意,就是安全點(diǎn),當(dāng)需要jvm做一些操作的時(shí)候,需要把當(dāng)前正在運(yùn)行的線程進(jìn)入一個(gè)安全點(diǎn)的狀態(tài)(也可以說停止?fàn)顟B(tài)),這樣才能做一些安全的操作,比如線程的dump,堆棧的信息。

safe point 顧明思意,就是安全點(diǎn),當(dāng)需要jvm做一些操作的時(shí)候,需要把當(dāng)前正在運(yùn)行的線程進(jìn)入一個(gè)安全點(diǎn)的狀態(tài)(也可以說停止?fàn)顟B(tài)),這樣才能做一些安全的操作,比如線程的dump,堆棧的信息。

在jvm里面通常vm_thread(我們一直在談?wù)摰淖鲆恍儆趘m 份內(nèi)事情的線程) 和cms_thread(內(nèi)存回收的線程)做的操作,是需要將其他的線程通過調(diào)用SafepointSynchronize::begin 和 SafepointSynchronize:end來實(shí)現(xiàn)讓其他的線程進(jìn)入或者退出safe point 的狀態(tài)。

通常safepoint 的有三種狀態(tài)

 

_not_synchronized

說明沒有任何打斷現(xiàn)在所有線程運(yùn)行的操作,也就是vm thread, cms thread 沒有接到操作的指令

_synchronizing

vm thread,cms thread 接到操作指令,正在等待所有線程進(jìn)入safe point

_synchronized

所有線程進(jìn)入safe point, vm thread, cms thread 可以開始指令操作

 

Java線程的狀態(tài)

通常在java 進(jìn)程中的Java 的線程有幾個(gè)不同的狀態(tài),如何讓這些線程進(jìn)入safepoint 的狀態(tài)中,jvm是采用不同的方式

a. 正在解釋執(zhí)行

由于java是解釋性語言,而線程在解釋java 字節(jié)碼的時(shí)候,需要dispatch table,記錄方法地址進(jìn)行跳轉(zhuǎn)的,那么這樣讓線程進(jìn)入停止?fàn)顟B(tài)就比較容易了,只要替換掉dispatch table 就可以了,讓線程知道當(dāng)前進(jìn)入softpoint 狀態(tài)。

java里會(huì)設(shè)置3個(gè)DispatchTable,  _active_table,  _normal_table, _safept_table

_active_table 正在解釋運(yùn)行的線程使用的dispatch table

_normal_table 就是正常運(yùn)行的初始化的dispatch table

_safept_table safe point需要的dispatch table

解釋運(yùn)行的線程一直都在使用_active_table,關(guān)鍵處就是在進(jìn)入saftpoint 的時(shí)候,用_safept_table替換_active_table, 在退出saftpoint 的時(shí)候,使用_normal_table來替換_active_table。

具體實(shí)現(xiàn)可以查看源碼

  1. void TemplateInterpreter::notice_safepoints() {  
  2.   if (!_notice_safepoints) {  
  3.     // switch to safepoint dispatch table  
  4.     _notice_safepoints = true;  
  5.     copy_table((address*)&_safept_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address));  
  6.   }  
  7. }  
  8.  
  9. // switch from the dispatch table which notices safepoints back to the  
  10. // normal dispatch table.  So that we can notice single stepping points,  
  11. // keep the safepoint dispatch table if we are single stepping in JVMTI.  
  12. // Note that the should_post_single_step test is exactly as fast as the  
  13. // JvmtiExport::_enabled test and covers both cases.  
  14. void TemplateInterpreter::ignore_safepoints() {  
  15.   if (_notice_safepoints) {  
  16.     if (!JvmtiExport::should_post_single_step()) {  
  17.       // switch to normal dispatch table  
  18.       _notice_safepoints = false;  
  19.       copy_table((address*)&_normal_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address));  
  20.     }  
  21.   }  

b. 運(yùn)行在native code

如果線程運(yùn)行在native code的時(shí)候,vm thread 是不需要等待線程執(zhí)行完的,只需要在從native code 返回的時(shí)候去判斷一下 _state 的狀態(tài)就可以了。

在方法體里就是前面博客也出現(xiàn)過的 SafepointSynchronize::do_call_back()

  1. inline static bool do_call_back() {  
  2.   return (_state != _not_synchronized);  

判斷了_state 不是_not_synchronized狀態(tài)

為了能讓線程從native code 回到j(luò)ava 的時(shí)候?yàn)榱四茏x到/設(shè)置正確線程的狀態(tài),通常的解決方法使用memory barrier,java 使用OrderAccess::fence(); 在匯編里使用__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory"); 保證從內(nèi)存里讀到正確的值,但是這種方法嚴(yán)重影響系統(tǒng)的性能,于是java使用了每個(gè)線程都有獨(dú)立的內(nèi)存頁來設(shè)置狀態(tài)。通過使用使用參數(shù)-XX:+UseMembar 參數(shù)使用memory barrier,默認(rèn)是不打開的,也就是使用獨(dú)立的內(nèi)存頁來設(shè)置狀態(tài)。

c. 運(yùn)行編譯的代碼

1. Poling page 頁面

Poling page是在jvm初始化啟動(dòng)的時(shí)候會(huì)初始化的一個(gè)單獨(dú)的內(nèi)存頁面,這個(gè)頁面是讓運(yùn)行的編譯過的代碼的線程進(jìn)入停止?fàn)顟B(tài)的關(guān)鍵。

在linux里面使用了mmap初始化,源碼如下

  1. address polling_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -10); 

2. 編譯

java 的JIT 會(huì)直接編譯一些熱門的源碼到機(jī)器碼,直接執(zhí)行而不需要在解釋執(zhí)行從而提高效率,在編譯的代碼中,當(dāng)函數(shù)或者方法塊返回的時(shí)候會(huì)去訪問一個(gè)內(nèi)存poling頁面。

x86架構(gòu)下

  1. void LIR_Assembler::return_op(LIR_Opr result) {  
  2.   assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == rax, "word returns are in rax,");  
  3.   if (!result->is_illegal() && result->is_float_kind() && !result->is_xmm_register()) {  
  4.     assert(result->fpu() == 0"result must already be on TOS");  
  5.   }  
  6.  
  7.   // Pop the stack before the safepoint code  
  8.   __ remove_frame(initial_frame_size_in_bytes());  
  9.  
  10.   bool result_is_oop = result->is_valid() ? result->is_oop() : false;  
  11.  
  12.   // Note: we do not need to round double result; float result has the right precision  
  13.   // the poll sets the condition code, but no data registers  
  14.   AddressLiteral polling_page(os::get_polling_page() + (SafepointPollOffset % os::vm_page_size()),  
  15.                               relocInfo::poll_return_type);  
  16.  
  17.   // NOTE: the requires that the polling page be reachable else the reloc  
  18.   // goes to the movq that loads the address and not the faulting instruction  
  19.   // which breaks the signal handler code  
  20.  
  21.   __ test32(rax, polling_page);  
  22.  
  23.   __ ret(0);  

在前面提到的SafepointSynchronize::begin 函數(shù)源碼中

  1. if (UseCompilerSafepoints && DeferPollingPageLoopCount < 0) {  
  2.   // Make polling safepoint aware  
  3.   guarantee (PageArmed == 0"invariant") ;  
  4.   PageArmed = 1 ;  
  5.   os::make_polling_page_unreadable();  

這里提到了2個(gè)參數(shù) UseCompilerSafepoints 和 DeferPollingPageLoopCount ,在默認(rèn)的情況下這2個(gè)參數(shù)是true和-1

函數(shù)體將會(huì)調(diào)用os:make_polling_page_unreadable();在linux os 下具體實(shí)現(xiàn)是調(diào)用了mprotect(bottom,size,prot) 使polling 內(nèi)存頁變成不可讀。

3. 信號(hào)

到當(dāng)編譯好的程序嘗試在去訪問這個(gè)不可讀的polling頁面的時(shí)候,在系統(tǒng)級(jí)別會(huì)產(chǎn)生一個(gè)錯(cuò)誤信號(hào)SIGSEGV, 可以參考筆者的一篇博客中曾經(jīng)講過java 的信號(hào)處理,可以知道信號(hào)SIGSEGV的處理函數(shù)在x86體系下見下源碼:

  1. JVM_handle_linux_signal(int sig,  
  2.                         siginfo_t* info,  
  3.                         void* ucVoid,  
  4.                         int abort_if_unrecognized){  
  5.    ....  
  6.    if (sig == SIGSEGV && os::is_poll_address((address)info->si_addr)) {  
  7.         stub = SharedRuntime::get_poll_stub(pc);  
  8.       }   
  9.    ....  

在linux x86,64 bit的體系中,poll stub 的地址 就是 SafepointSynchronize::handle_polling_page_exception 詳細(xì)程序可見shareRuntime_x86_64.cpp

回到safepoint.cpp中,SafepointSynchronize::handle_polling_page_exception通過取出線程的safepoint_stat,調(diào)用函數(shù)void ThreadSafepointState::handle_polling_page_exception,***通過調(diào)用SafepointSynchronize::block(thread()); 來block當(dāng)前線程。

 

d. block 狀態(tài)

當(dāng)線程進(jìn)入block狀態(tài)的時(shí)候,繼續(xù)保持block狀態(tài)。

原文鏈接:http://blog.csdn.net/raintungli/article/details/7162468

【系列文章】

  1. Java開源工具在linux上的源碼分析(一):跟蹤方式
  2. Java開源工具在linux上的源碼分析(二):信號(hào)處理
  3. Java開源工具在linux上的源碼分析(三):執(zhí)行的線程vm thread
  4. Java開源工具在linux上的源碼分析(五):-F參數(shù)的bug
  5. Java開源工具在linux上的源碼分析(六):符號(hào)表的讀取
責(zé)任編輯:林師授 來源: raintungli的博客
相關(guān)推薦

2012-03-02 12:14:19

JavaJstackJmap

2012-03-02 12:20:21

Javajmapjstack

2012-03-02 12:25:07

Javajmapjstack

2012-03-02 12:38:49

Javajmapjstack

2012-03-02 13:29:38

Javajmapjstack

2022-06-26 18:09:43

Linux開源

2019-10-16 17:00:51

LinuxUbuntuVMware

2020-05-09 12:01:40

Linux開源軟件SDN

2022-02-18 15:19:52

日志收集工具開源

2010-01-27 09:58:59

Linuxunix程序日志

2021-03-09 11:25:04

Linux開源工具服務(wù)器

2018-05-30 09:00:00

2019-08-01 09:52:46

LinuxNetData性能監(jiān)控工具

2012-05-22 00:28:21

JavaJava開源開源工具

2021-08-31 09:41:57

LinuxiPhone開源工具

2021-09-01 09:47:25

Linux 工具 開發(fā)

2022-06-06 14:20:25

個(gè)人財(cái)務(wù)開源預(yù)算

2019-05-23 14:36:24

LinuxSOSReportxsos

2021-11-25 09:25:51

Linux服務(wù)器開源工具

2017-01-12 15:58:17

Linux死鎖分析方法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)