麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

JavaMain如何被執(zhí)行?

2019-11-14 15:11:11
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  java應(yīng)用程序的啟動(dòng)在在/hotspot/src/share/tools/launcher/java.c的main()函數(shù)中,而在虛擬機(jī)初始化過(guò)程中,將創(chuàng)建并啟動(dòng)Java的Main線(xiàn)程。最后將調(diào)用JNIEnv的CallStaticVoidMethod()來(lái)執(zhí)行main方法。

CallStaticVoidMethod()對(duì)應(yīng)的jni函數(shù)為jni_CallStaticVoidMethod,定義在/hotspot/src/share/vm/PRims/jni.cpp中,而jni_CallStaticVoidMethod()又調(diào)用了jni_invoke_static(),jni_invoke_static()通過(guò)JavaCalls的call()發(fā)起對(duì)Java方法的調(diào)用

  所有來(lái)自虛擬機(jī)對(duì)Java函數(shù)的調(diào)用最終都將由JavaCalls模塊來(lái)完成,JavaCalls將通過(guò)call_helper()來(lái)執(zhí)行Java方法并返回調(diào)用結(jié)果,并最終調(diào)用StubRoutines::call_stub()來(lái)執(zhí)行Java方法

 1 // do call 2   { JavaCallWrapper link(method, receiver, result, CHECK); 3     { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner 4  5       StubRoutines::call_stub()( 6         (address)&link, 7         // (intptr_t*)&(result->_value), // see NOTE above (compiler problem) 8         result_val_address,          // see NOTE above (compiler problem) 9         result_type,10         method(),11         entry_point,12         args->parameters(),13         args->size_of_parameters(),14         CHECK15       );16 17       result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)18       // Preserve oop return value across possible gc points19       if (oop_result_flag) {20         thread->set_vm_result((oop) result->get_jobject());21       }22     }23   }

  call_stub()定義在/hotspot/src/share/vm/runtime/stubRoutines.h中,實(shí)際上返回的就是CallStub函數(shù)指針_call_stub_entry,該指針指向call_stub的匯編實(shí)現(xiàn)的目標(biāo)代碼指令地址,即call_stub的例程入口。

// Calls to Java  typedef void (*CallStub)(    address   link,    intptr_t* result,    BasicType result_type,    methodOopDesc* method,    address   entry_point,    intptr_t* parameters,    int       size_of_parameters,    TRAPS  );  static CallStub call_stub()   { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); }

  在分析call_stub的匯編代碼之前,先了解下x86寄存器和棧幀以及函數(shù)調(diào)用的相關(guān)知識(shí)。 
  x86-64的所有寄存器都是與機(jī)器字長(zhǎng)(數(shù)據(jù)總線(xiàn)位寬)相同,即64位的,x86-64將x86的8個(gè)32位通用寄存器擴(kuò)展為64位(eax、ebx、ecx、edx、eci、edi、ebp、esp),并且增加了8個(gè)新的64位寄存器(r8-r15),在命名方式上,也從”exx”變?yōu)?rdquo;rxx”,但仍保留”exx”進(jìn)行32位操作,下表描述了各寄存器的命名和作用

 

 此外,還有16個(gè)128位的XMM寄存器,分別為xmm0-15,x84-64的寄存器遵循調(diào)用約定(Calling Conventions): 

https://msdn.microsoft.com/en-US/library/zthk2dkh(v=vs.80).asp
1.參數(shù)傳遞: 
  (1).前4個(gè)參數(shù)的int類(lèi)型分別通過(guò)rcx、rdx、r8、r9傳遞,多余的在??臻g上傳遞(從右向左依次入棧),寄存器所有的參數(shù)都是向右對(duì)齊的(低位對(duì)齊) 
  (2).浮點(diǎn)數(shù)類(lèi)型的參數(shù)通過(guò)xmm0-xmm3傳遞,注意不同類(lèi)型的參數(shù)占用的寄存器序號(hào)是根據(jù)參數(shù)的序號(hào)來(lái)決定的,比如add(int,double,float,int)就分別保存在rcx、xmm1、xmm2、r9寄存器中 
  (3).8/16/32/64類(lèi)型的結(jié)構(gòu)體或共用體和_m64類(lèi)型將使用rcx、rdx、r8、r9直接傳遞,而其他類(lèi)型將會(huì)通過(guò)指針引用的方式在這4個(gè)寄存器中傳遞 
  (4).被調(diào)用函數(shù)當(dāng)需要時(shí)要把寄存器中的參數(shù)移動(dòng)到??臻g中(shadow space) 
2.返回值傳遞 
  (1).對(duì)于可以填充為64位的返回值(包括_m64)將使用rax進(jìn)行傳遞 
  (2).對(duì)于_m128(i/d)以及浮點(diǎn)數(shù)類(lèi)型將使用xmm0傳遞 
  (3).對(duì)于64位以上的返回值,將由調(diào)用函數(shù)在棧上為其分配空間,并將其指針保存在rcx中作為”第一個(gè)參數(shù)”,而傳入?yún)?shù)將依次右移,最后函數(shù)調(diào)用完后,由rax返回該空間的指針 
  (4).用戶(hù)定義的返回值類(lèi)型長(zhǎng)度必須是1、2、4、8、16、32、64 
3.調(diào)用者/被調(diào)用者保存寄存器 
  調(diào)用者保存寄存器:rax、rcx、rdx、r8-r11都認(rèn)為是易失型寄存器(volatile),這些寄存器隨時(shí)可能被用到,這些寄存器將由調(diào)用者自行維護(hù),當(dāng)調(diào)用其他函數(shù)時(shí),被調(diào)用函數(shù)對(duì)這些寄存器的操作并不會(huì)影響調(diào)用函數(shù)(即這些寄存器的作用范圍僅限于當(dāng)前函數(shù))。 
  被調(diào)用者保存寄存器:rbx、rbp、rdi、rsi、r12-r15、xmm6-xmm15都是非易失型寄存器(non-volatile),調(diào)用其他函數(shù)時(shí),這些寄存器的值可能在調(diào)用返回時(shí)還需要用,那么被調(diào)用函數(shù)就必須將這些寄存器的值保存起來(lái),當(dāng)要返回時(shí),恢復(fù)這些寄存器的值(即這些寄存器的作用范圍是跨函數(shù)調(diào)用的)。

  以如下程序?yàn)槔治龊瘮?shù)調(diào)用的棧幀布局:

 1 double func(int param_i1, float param_f1, double param_d1, int param_i2, double param_d2) 2  3 { 4     int local_i1, local_i2; 5     float local_f1; 6     double local_d1; 7     double local_d2 = 3.0; 8     local_i1 = param_i1; 9     local_i2 = param_i2;10     local_f1 = param_f1;11     local_d1 = param_d1;12     return local_d1 + local_f1 * (local_i2 - local_i1) - param_d2 + local_d2;13 }14 15 int main()16 17 {18     double res;19     res = func(1, 1.0, 2.0, 3, 3.0);20     return 0;21 }

main函數(shù)調(diào)用func之前的匯編代碼如下:

main:    pushq   %rbp            //保存rbp    .seh_pushreg    %rbp    movq    %rsp, %rbp      //更新?;?/span>    .seh_setframe   %rbp, 0    subq    $80, %rsp          .seh_stackalloc 80      //main棧需要80字節(jié)的棧空間    .seh_endprologue    call    __main    movabsq $4611686018427387904, %rdx //0x4000000000000000,即浮點(diǎn)數(shù)2.0    movabsq $4613937818241073152, %rax //0x3000000000000000,即浮點(diǎn)數(shù)3.0    movq    %rax, 32(%rsp)          //第5個(gè)參數(shù)3.0,即param_d2保存在??臻g上    movl    $3, %r9d               //第4個(gè)參數(shù)3,即param_i2保存在r9d中(r9的低32位)    movq    %rdx, -24(%rbp)             movsd   -24(%rbp), %xmm2        //第3個(gè)參數(shù)2.0,即param_d1保存在xmm2中    movss   .LC2(%rip), %xmm1       //第2個(gè)參數(shù)1.0(0x3f800000),保存在xmm1中    movl    $1, %ecx               //第1個(gè)參數(shù)1,保存在ecx中(rcx的低32位)    call    func

func函數(shù)返回后,main函數(shù)將從xmm0中取出返回結(jié)果

call    func    movq    %xmm0, %rax             //保存結(jié)果    movq    %rax, -8(%rbp)              movl    $0, %eax               //清空eax,回收main棧,恢復(fù)棧頂?shù)刂?/span>    addq    $80, %rsp    popq    %rbp    ret

func函數(shù)的棧和操作數(shù)準(zhǔn)備如下:

func:    pushq   %rbp        //保存rbp(main函數(shù)棧的基址)    .seh_pushreg    %rbp    movq    %rsp, %rbp      //將main棧的棧頂指針作為被調(diào)用函數(shù)的?;?/span>    .seh_setframe   %rbp, 0    subq    $32, %rsp  //func棧需要32字節(jié)的??臻g    .seh_stackalloc 32    .seh_endprologue    movl    %ecx, 16(%rbp)  //將4個(gè)參數(shù)移動(dòng)到棧底偏移16-40的空間(main棧的shadow space)    movss   %xmm1, 24(%rbp)    movsd   %xmm2, 32(%rbp)    movl    %r9d, 40(%rbp)    movabsq $4613937818241073152, %rax //本地變量local_d2,即浮點(diǎn)數(shù)3.0    movq    %rax, -8(%rbp)  //5個(gè)局部變量    movl    16(%rbp), %eax    movl    %eax, -12(%rbp)    movl    40(%rbp), %eax    movl    %eax, -16(%rbp)    movl    24(%rbp), %eax    movl    %eax, -20(%rbp)    movq    32(%rbp), %rax    movq    %rax, -32(%rbp)

隨后的func的運(yùn)算過(guò)程如下:

   movl    -16(%rbp), %eax //local_i2 - local_i1    subl    -12(%rbp), %eax    pxor    %xmm0, %xmm0    //準(zhǔn)備xmm0寄存器,按位異或,xmm0清零    cvtsi2ss    %eax, %xmm0    mulss   -20(%rbp), %xmm0    //local_f1 * (local_i2 - local_i1)    cvtss2sd    %xmm0, %xmm0    addsd   -32(%rbp), %xmm0    //local_d1 + local_f1 * (local_i2 - local_i1)    subsd   48(%rbp), %xmm0     //local_d1 + local_f1 * (local_i2 - local_i1) - param_d2    addsd   -8(%rbp), %xmm0     //local_d1 + local_f1 * (local_i2 - local_i1) - param_d2 + local_d2    addq    $32, %rsp      //回收f(shuō)unc棧,恢復(fù)棧頂?shù)刂?/span>    popq    %rbp    ret

根據(jù)以上代碼分析,大概得出該程序調(diào)用棧結(jié)構(gòu):

這里沒(méi)有考慮func函數(shù)再次調(diào)用其他函數(shù)而準(zhǔn)備操作數(shù)的棧內(nèi)容的情況,但結(jié)合main函數(shù)棧,大致可以得出棧的通用結(jié)構(gòu)如下: 

call_stub由generate_call_stub()解釋成匯編代碼,有興趣的可以繼續(xù)閱讀call_stub的匯編代碼進(jìn)行分析。 
下面對(duì)call_stub的匯編部分進(jìn)行分析: 
先來(lái)看下call_stub的調(diào)用棧結(jié)構(gòu):(注:本文實(shí)驗(yàn)是在windows_64位平臺(tái)上實(shí)現(xiàn)的)

// Call stubs are used to call Java from C  //    return_from_Java 是緊跟在call *%eax后面的那條指令的地址  //     [ return_from_Java      ] <--- rsp  // -28 [ arguments             ] <-- rbp - 0xe8  // -26 [ saved xmm15           ] <-- rbp - 0xd8  // -24 [ saved xmm14           ] <-- rbp - 0xc8  // -22 [ saved xmm13           ] <-- rbp - 0xb8  // -20 [ saved xmm12           ] <-- rbp - 0xa8  // -18 [ saved xmm11           ] <-- rbp - 0x98  // -16 [ saved xmm10           ] <-- rbp - 0x88  // -14 [ saved xmm9            ] <-- rbp - 0x78  // -12 [ saved xmm8            ] <-- rbp - 0x68  // -10 [ saved xmm7            ] <-- rbp - 0x58  // -9  [ saved xmm6            ] <-- rbp - 0x48   // -7  [ saved r15             ] <-- rbp - 0x38  // -6  [ saved r14             ] <-- rbp - 0x30  // -5  [ saved r13             ] <-- rbp - 0x28  // -4  [ saved r12             ] <-- rbp - 0x20  // -3  [ saved rdi             ] <-- rbp - 0x18  // -2  [ saved rsi             ] <-- rbp - 0x10    // -1  [ saved rbx             ] <-- rbp - 0x8  //  0  [ saved rbp             ] <--- rbp,  //  1 [ return address       ]  <--- rbp + 0x08  //  2 [ ptr. to call wrapper ]  <--- rbp + 0x10  //  3 [ result               ]  <--- rbp + 0x18  //  4 [ result_type          ]  <--- rbp + 0x20  //  5 [ method               ]  <--- rbp + 0x28  //  6 [ entry_point          ]  <--- rbp + 0x30  //  7 [ parameters           ]  <--- rbp + 0x38  //  8 [ parameter_size       ]  <--- rbp + 0x40  //  9 [ thread               ]  <--- rbp + 0x48

1.根據(jù)函數(shù)調(diào)用棧的結(jié)構(gòu): 
在被調(diào)函數(shù)棧幀的棧底 %rbp + 8(棧地址向下增長(zhǎng),堆地址向上增長(zhǎng),棧底的正偏移值指向調(diào)用函數(shù)棧幀內(nèi)容)保存著被調(diào)函數(shù)的傳入?yún)?shù),這里即: 
JavaCallWrapper指針、返回結(jié)果指針、返回結(jié)果類(lèi)型、被調(diào)用方法的methodOop、被調(diào)用方法的解釋代碼的入口地址、參數(shù)地址、參數(shù)個(gè)數(shù)。

StubRoutines::call_stub [0x0000000002400567, 0x00000000024006cb[ (356 bytes)  //保存bp  0x0000000002400567: push   %rbp  //更新棧頂?shù)刂?             0x0000000002400568: mov    %rsp,%rbp  //call_stub需要的棧空間大小為0xd8  0x000000000240056b: sub    $0xd8,%rsp

2.rcx、rdx、r8d、r9d分別保存著傳入call_stub的前4個(gè)參數(shù),現(xiàn)在需要將其復(fù)制到棧上的shadow space中

  //分別使用rcx、rdx、r8、r9來(lái)保存第1、2、3、4個(gè)參數(shù),多出來(lái)的其他參數(shù)用??臻g來(lái)傳遞  //使用xmm0-4來(lái)傳遞第1-4個(gè)浮點(diǎn)數(shù)參數(shù)  //這里將參數(shù)復(fù)制到棧空間,這樣call_stub的所有參數(shù)就在rbp + 0x10 ~ 0x48??臻g上  0x0000000002400572: mov    %r9,0x28(%rbp)  0x0000000002400576: mov    %r8d,0x20(%rbp)  0x000000000240057a: mov    %rdx,0x18(%rbp)  0x000000000240057e: mov    %rcx,0x10(%rbp)

3.將被調(diào)用者保存寄存器的值壓入call_stub棧中:

;; save registers:  //依次保存rbx、rsi、rdi這三個(gè)被調(diào)用者保存的寄存器,隨后保存r12-r15、XMM寄存器組xmm6-xmm15  0x0000000002400582: mov    %rbx,-0x8(%rbp)  0x0000000002400586: mov    %r12,-0x20(%rbp)  0x000000000240058a: mov    %r13,-0x28(%rbp)  0x000000000240058e: mov    %r14,-0x30(%rbp)  0x0000000002400592: mov    %r15,-0x38(%rbp)  0x0000000002400596: vmovdqu %xmm6,-0x48(%rbp)  0x000000000240059b: vmovdqu %xmm7,-0x58(%rbp)  0x00000000024005a0: vmovdqu %xmm8,-0x68(%rbp)  0x00000000024005a5: vmovdqu %xmm9,-0x78(%rbp)  0x00000000024005aa: vmovdqu %xmm10,-0x88(%rbp)  0x00000000024005b2: vmovdqu %xmm11,-0x98(%rbp)  0x00000000024005ba: vmovdqu %xmm12,-0xa8(%rbp)  0x00000000024005c2: vmovdqu %xmm13,-0xb8(%rbp)  0x00000000024005ca: vmovdqu %xmm14,-0xc8(%rbp)  0x00000000024005d2: vmovdqu %xmm15,-0xd8(%rbp)  0x00000000024005da: mov    %rsi,-0x10(%rbp)  0x00000000024005de: mov    %rdi,-0x18(%rbp)  //棧底指針的0x48偏移保存著thread對(duì)象,0x6d01a2c3(%rip)為異常處理入口  0x00000000024005e2: mov    0x48(%rbp),%r15  0x00000000024005e6: mov    0x6d01a2c3(%rip),%r12        # 0x000000006f41a8b0

4.call_stub的參數(shù)保存著Java方法的參數(shù),現(xiàn)在就需要將參數(shù)壓入call_stub棧中

/棧底指針的0x40偏移保存著參數(shù)的個(gè)數(shù)  0x00000000024005ed: mov    0x40(%rbp),%r9d  //若參數(shù)個(gè)數(shù)為0,則直接跳轉(zhuǎn)0x000000000240060d準(zhǔn)備調(diào)用Java方法  0x00000000024005f1: test   %r9d,%r9d  0x00000000024005f4: je     0x000000000240060d  //若參數(shù)個(gè)數(shù)不為0,則遍歷參數(shù),將所有參數(shù)壓入本地棧  //其中棧底指針的0x38偏移保存著參數(shù)的地址,edx將用作循環(huán)的迭代器  0x00000000024005fa: mov    0x38(%rbp),%r8  0x00000000024005fe: mov    %r9d,%edx  ;; loop:  //從第一個(gè)參數(shù)開(kāi)始,將Java方法的參數(shù)壓人本地棧  /*       *     i = parameter_size; //確保不等于0  *     do{  *       push(parameter[i]);  *       i--;  *     }while(i!=0);  */  0x0000000002400601: mov    (%r8),%rax  0x0000000002400604: add    $0x8,%r8  0x0000000002400608: dec    %edx  0x000000000240060a: push   %rax  0x000000000240060b: jne    0x0000000002400601

5.調(diào)用Java方法的解釋代碼

;; prepare entry:  //棧底指針的0x28和0x30偏移分別保存著被調(diào)用Java方法的methodOop指針和解釋代碼的入口地址  0x000000000240060d: mov    0x28(%rbp),%rbx  0x0000000002400611: mov    0x30(%rbp),%rdx  0x0000000002400615: mov    %rsp,%r13  //保存棧頂指針  ;; jump to run Java method:  0x0000000002400618: callq  *%rdx

6.準(zhǔn)備保存返回結(jié)果,這里需要先根據(jù)不同的返回類(lèi)型取出返回結(jié)果,然后保存到返回結(jié)果指針?biāo)赶虻奈恢?/p>

;; prepare to save result:  //棧底指針的0x18和0x20偏移分別保存著返回結(jié)果的指針和結(jié)果類(lèi)型  0x000000000240061a: mov    0x18(%rbp),%rcx  0x000000000240061e: mov    0x20(%rbp),%edx  ;; handle result accord to different result_type:  0x0000000002400621: cmp    $0xc,%edx  0x0000000002400624: je     0x00000000024006b7  0x000000000240062a: cmp    $0xb,%edx  0x000000000240062d: je     0x00000000024006b7  0x0000000002400633: cmp    $0x6,%edx  0x0000000002400636: je     0x00000000024006bc  0x000000000240063c: cmp    $0x7,%edx  0x000000000240063f: je     0x00000000024006c2  ;; save result for the other result_type:  0x0000000002400645: mov    %eax,(%rcx)

下面分別為返回結(jié)果類(lèi)型為long、float、double的情況

;; long 類(lèi)型返回結(jié)果保存:    0x00000000024006b7: mov    %rax,(%rcx)  0x00000000024006ba: jmp    0x0000000002400647  ;; float 類(lèi)型返回結(jié)果保存:    0x00000000024006bc: vmovss %xmm0,(%rcx)  0x00000000024006c0: jmp    0x0000000002400647  ;; double 類(lèi)型返回結(jié)果保存:    0x00000000024006c2: vmovsd %xmm0,(%rcx)  0x00000000024006c6: jmpq   0x0000000002400647

7.被調(diào)用者保存寄存器的恢復(fù),以及棧指針的復(fù)位

;; restore registers:  0x0000000002400647: lea    -0xd8(%rbp),%rsp  0x000000000240064e: vmovdqu -0xd8(%rbp),%xmm15  0x0000000002400656: vmovdqu -0xc8(%rbp),%xmm14  0x000000000240065e: vmovdqu -0xb8(%rbp),%xmm13  0x0000000002400666: vmovdqu -0xa8(%rbp),%xmm12  0x000000000240066e: vmovdqu -0x98(%rbp),%xmm11  0x0000000002400676: vmovdqu -0x88(%rbp),%xmm10  0x000000000240067e: vmovdqu -0x78(%rbp),%xmm9  0x0000000002400683: vmovdqu -0x68(%rbp),%xmm8  0x0000000002400688: vmovdqu -0x58(%rbp),%xmm7  0x000000000240068d: vmovdqu -0x48(%rbp),%xmm6  0x0000000002400692: mov    -0x38(%rbp),%r15  0x0000000002400696: mov    -0x30(%rbp),%r14  0x000000000240069a: mov    -0x28(%rbp),%r13  0x000000000240069e: mov    -0x20(%rbp),%r12  0x00000000024006a2: mov    -0x8(%rbp),%rbx  0x00000000024006a6: mov    -0x18(%rbp),%rdi  0x00000000024006aa: mov    -0x10(%rbp),%rsi  ;; back to old(caller) stack frame:  0x00000000024006ae: add    $0xd8,%rsp //棧頂指針復(fù)位  0x00000000024006b5: pop    %rbp //棧底指針復(fù)位  0x00000000024006b6: retq  

歸納出call_stub棧結(jié)構(gòu)如下: 

8.對(duì)于不同的Java方法,虛擬機(jī)在初始化時(shí)會(huì)生成不同的方法入口例程 
(method entry point)來(lái)準(zhǔn)備棧幀,這里以較常被使用的zerolocals方法入口為例,分析Java方法的棧幀結(jié)構(gòu)與調(diào)用過(guò)程,入口例程目標(biāo)代碼的產(chǎn)生在InterpreterGenerator::generate_normal_entry()中: 
(1).根據(jù)之前的分析,初始的棧結(jié)構(gòu)如下: 

獲取傳入?yún)?shù)數(shù)量到rcx中:

address InterpreterGenerator::generate_normal_entry(bool synchronized) {  // determine code generation flags  bool inc_counter  = UseCompiler || CountCompiledCalls;  // ebx: methodOop  // r13: sender sp  address entry_point = __ pc();  const Address size_of_parameters(rbx,                                   methodOopDesc::size_of_parameters_offset());  const Address size_of_locals(rbx, methodOopDesc::size_of_locals_offset());  const Address invocation_counter(rbx,                                   methodOopDesc::invocation_counter_offset() +                                   InvocationCounter::counter_offset());  const Address access_flags(rbx, methodOopDesc::access_flags_offset());  // get parameter size (always needed)  __ load_unsigned_short(rcx, size_of_parameters);

其中methodOop指針被保存在rbx中,調(diào)用Java方法的sender sp被保存在r13中,參數(shù)大小保存在rcx中 
(2).獲取局部變量區(qū)的大小,保存在rdx中,并減去參數(shù)數(shù)量,將除參數(shù)以外的局部變量數(shù)量保存在rdx中(雖然參數(shù)作為局部變量是方法的一部分,但參數(shù)由調(diào)用者提供,這些參數(shù)應(yīng)有調(diào)用者棧幀而非被調(diào)用者棧幀維護(hù),即被調(diào)用者棧幀只需要維護(hù)局部變量中除了參數(shù)的部分即可)

// rbx: methodOop  // rcx: size of parameters  // r13: sender_sp (could differ from sp+WordSize if we were called via c2i )  __ load_unsigned_short(rdx, size_of_locals); // get size of locals in words  __ subl(rdx, rcx); // rdx = no. of additional locals

(3).對(duì)??臻g大小進(jìn)行檢查,判斷是否會(huì)發(fā)生棧溢出

// see if we've got enough room on the stack for locals plus overhead.  generate_stack_overflow_check();

(4).獲取返回地址,保存在rax中(注意此時(shí)棧頂為調(diào)用函數(shù)call指令后下一條指令的地址)

// get return address  __ pop(rax);

(5).由于參數(shù)在棧中由低地址向高地址是以相反的順序存放的,所以第一個(gè)參數(shù)的地址應(yīng)該是 rsp+rcx*8-8(第一個(gè)參數(shù)地址范圍為 rsp+rcx*8-8 ~ rsp+rcx*8),將其保存在r14中

// compute beginning of parameters (r14)  __ lea(r14, Address(rsp, rcx, Address::times_8, -wordSize))

(6).為除參數(shù)以外的局部變量分配??臻g,若這些局部變量數(shù)量為0,那么就跳過(guò)這一部分處理,否則,將壓入 maxlocals - param_size個(gè)0,以初始化這些局部變量

//該部分為一個(gè)loop循環(huán)// rdx - # of additional locals  // allocate space for locals  // explicitly initialize locals  {    Label exit, loop;    __ testl(rdx, rdx);    __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0    __ bind(loop);    __ push((int) NULL_WORD); // initialize local variables    __ decrementl(rdx); // until everything initialized    __ jcc(Assembler::greater, loop);    __ bind(exit);  }

這時(shí)棧的層次如下:

(7).將方法的調(diào)用次數(shù)保存在rcx/ecx中

// (pre-)fetch invocation count  if (inc_counter) {    __ movl(rcx, invocation_counter);  }

(8).初始化當(dāng)前方法的棧幀

// initialize fixed part of activation frame  generate_fixed_frame(false);

generate_fixed_frame()的實(shí)現(xiàn)如下:

  __ push(rax);        // save return address  __ enter();          // save old & set new rbp  __ push(r13);        // set sender sp  __ push((int)NULL_WORD); // leave last_sp as null  __ movptr(r13, Address(rbx, methodOopDesc::const_offset()));      // get constMethodOop  __ lea(r13, Address(r13, constMethodOopDesc::codes_offset())); // get codebase  __ push(rbx);

保存返回地址,為被調(diào)用的Java方法準(zhǔn)備棧幀,并將sender sp指針、last_sp(設(shè)置為0)壓入棧,根據(jù)methodOop的constMethodOop成員將字節(jié)碼指針保存到r13寄存器中,并將methodOop壓入棧

} else {    __ push(0); //methodData  }  __ movptr(rdx, Address(rbx, methodOopDesc::constants_offset()));  __ movptr(rdx, Address(rdx, constantPoolOopDesc::cache_offset_in_bytes()));  __ push(rdx); // set constant pool cache  __ push(r14); // set locals pointer  if (native_call) {    __ push(0); // no bcp  } else {    __ push(r13); // set bcp  }  __ push(0); // reserve word for pointer to expression stack bottom  __ movptr(Address(rsp, 0), rsp); // set expression stack bottom}

將methodData以0為初始值壓入棧,根據(jù)methodOop的ConstantPoolOop成員將常量池緩沖地址壓入棧,r14中保存著局部變量區(qū)(第一個(gè)參數(shù)的地址)指針,將其壓入棧,此外如果調(diào)用的是native調(diào)用,那么字節(jié)碼指針部分為0,否則正常將字節(jié)碼指針壓入棧,最后為棧留出一個(gè)字的表達(dá)式棧底空間,并更新rsp

最后棧的空間結(jié)構(gòu)如下: 

(9).增加方法的調(diào)用計(jì)數(shù)

// increment invocation count & check for overflow  Label invocation_counter_overflow;  Label profile_method;  Label profile_method_continue;  if (inc_counter) {    generate_counter_incr(&invocation_counter_overflow,                          &profile_method,                          &profile_method_continue);    if (ProfileInterpreter) {      __ bind(profile_method_continue);    }  }

(當(dāng)調(diào)用深度過(guò)大會(huì)拋出StackOverFlow異常) 
(10).同步方法的Monitor對(duì)象分配和方法的加鎖(在匯編部分分析中沒(méi)有該部分,如果對(duì)同步感興趣的請(qǐng)自行分析)

if (synchronized) {    // Allocate monitor and lock method    lock_method();

(11).JVM工具接口部分

// jvmti support  __ notify_method_entry();

(12).跳轉(zhuǎn)到第一條字節(jié)碼的本地代碼處執(zhí)行

 __ dispatch_next(vtos);

以上分析可能略顯復(fù)雜,但重要的是明白方法的入口例程是如何為Java方法構(gòu)造新的棧幀,從而為字節(jié)碼的運(yùn)行提供調(diào)用棧環(huán)境。

method entry point匯編代碼的分析可以參考隨后的一篇文章。


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 亚洲第一色婷婷 | av成人免费观看 | 369看片你懂的小视频在线观看 | 亚洲欧美不卡视频 | av在线免费播放 | 黄色网电影 | 国产精品成人一区二区三区电影毛片 | 成人做爰高潮片免费视频美国 | 逼片视频 | 老女人碰碰在线碰碰视频 | 亚洲第一黄色网 | 国产精品视频二区不卡 | 一级在线免费 | 精品成人免费一区二区在线播放 | 国产一区二区精品91 | 亚洲人成中文字幕在线观看 | 欧美一区高清 | 欧美日韩亚洲成人 | 噜噜在线视频 | 在线成人免费网站 | 在线看毛片的网站 | 久久人人爽人人爽人人片av免费 | 久久久久久久久久久久久久av | 在线视频观看成人 | 精品国产专区 | 久久精品欧美一区二区 | 久草在线视频看看 | 国产韩国精品一区二区三区久久 | 国产一区二区三区撒尿在线 | 欧美特黄一级高清免费的香蕉 | 欧洲伊人网| 国产一区毛片 | 午夜精品在线视频 | 久久久久久久一区二区三区 | 毛片免费视频观看 | 精品久久久久久久久久久久久 | 麻豆视频国产在线观看 | 亚洲成人福利在线观看 | 一级网站 | 国产精品片一区二区三区 | 狠狠干91 |