23 方法调用的流程(invokestatic为例)

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了23 方法调用的流程(invokestatic为例)相关的知识,希望对你有一定的参考价值。

前言

之前看到一篇文章, java 反射调用 private 相关 

里面 大佬大致截图 截了一下 为什么运行时生成的 GeneratedMethodAccessor 可以访问所有的方法 

呵呵 说实话 这个问题 之前还没有考虑到 

但是 大佬的截图 说是话 还是有些抽象, 没有和具体的 运行时的来龙去脉 连接在一起 

但是 在排查整个流程之前, 我们需要知道 方法调用 指令处理了那些事情  

呵呵 这个就是 本文的重点了 

本文 主要是以 invokestatic 为例来走一下 大致的流程  

有一些基础知识 可以参考一下文章, 以及一下文章的参考文章(recurse) 

14 HelloWorld的字节码的编译执行的调试

15 main方法的栈帧信息

07 运行时常量池索引的 rewrite

以下代码, 截图 基于 jdk9 

测试用例

package com.hx.test05;

/**
 * InvokeStatic
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020-05-01 15:03
 */
public class Test18InvokeStatic 

  // Test18InvokeStatic
  public static void main(String[] args) 

    foo();

  

  // foo
  public static void foo() 

  


呵呵 我们主要走的是 invokestatic 的整个流程, 因此 这个测试用例 很简单 

对应的字节码信息如下, 下面参照可能需要使用到 

master:classes jerry$ javap -c -v com/hx/test05/Test18InvokeStatic.class 
Classfile /Users/jerry/IdeaProjects/HelloWorld/target/classes/com/hx/test05/Test18InvokeStatic.class
  Last modified May 1, 2020; size 487 bytes
  MD5 checksum d8cf4aca9a312dae396d364dff4e4e53
  Compiled from "Test18InvokeStatic.java"
public class com.hx.test05.Test18InvokeStatic
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#19         // java/lang/Object."<init>":()V
   #2 = Methodref          #3.#20         // com/hx/test05/Test18InvokeStatic.foo:()V
   #3 = Class              #21            // com/hx/test05/Test18InvokeStatic
   #4 = Class              #22            // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               LocalVariableTable
  #10 = Utf8               this
  #11 = Utf8               Lcom/hx/test05/Test18InvokeStatic;
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               args
  #15 = Utf8               [Ljava/lang/String;
  #16 = Utf8               foo
  #17 = Utf8               SourceFile
  #18 = Utf8               Test18InvokeStatic.java
  #19 = NameAndType        #5:#6          // "<init>":()V
  #20 = NameAndType        #16:#6         // foo:()V
  #21 = Utf8               com/hx/test05/Test18InvokeStatic
  #22 = Utf8               java/lang/Object

  public com.hx.test05.Test18InvokeStatic();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/hx/test05/Test18InvokeStatic;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokestatic  #2                  // Method foo:()V
         3: return
      LineNumberTable:
        line 15: 0
        line 17: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;

  public static void foo();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 22: 0

SourceFile: "Test18InvokeStatic.java"

基于 lldb 的调试

还是同参考的一系列文章差不多, 我们这里 先来进行 lldb 的调试, 然后 之后再来分析 相关的代码 

(lldb)  p _active_table._table[9][184]
(address) $1 = 0x000000010584c8bf "L\\x89m?A\\x0f?U\\x01H\\x8bM??\\U0000008b\\\\???\\x10\\x81??
(lldb) b 0x000000010584c8bf
Breakpoint 3: address = 0x000000010584c8bf
(lldb) c
Process 3885 resuming
Process 3885 stopped
* thread #5, stop reason = breakpoint 3.1
    frame #0: 0x000000010584c8bf
->  0x10584c8bf: movq   %r13, -0x40(%rbp)
    0x10584c8c3: movzwl 0x1(%r13), %edx
    0x10584c8c8: movq   -0x30(%rbp), %rcx
    0x10584c8cc: shll   $0x2, %edx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x00007000063a3650
       rbx = 0x00000000000000b8
       rcx = 0x0000000000000008
       rdx = 0x0000000000000000
       rdi = 0x0000000101807000
       rsi = 0x0000000000000008
       rbp = 0x00007000063a3698
       rsp = 0x00007000063a3650
        r8 = 0x0000000000002901
        r9 = 0x0000000101807000
       r10 = 0x000000010430b270  libjvm.dylib`TemplateInterpreter::_active_table + 18432
       r11 = 0x0000000000000000
       r12 = 0x0000000000000000
       r13 = 0x000000011d369e70
       r14 = 0x00007000063a36a8
       r15 = 0x0000000101807000
       rip = 0x000000010584c8bf
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) x 0x000000011d369e70
0x11d369e70: b8 01 00 b1 ff 00 1e 1a 00 00 00 00 04 00 0e 00  ?..??...........
0x11d369e80: 0f 00 00 00 00 00 01 00 40 6f 23 04 01 00 00 00  ........@o#.....
(lldb) x 0x00007000063a3650 -c 0x100
0x7000063a3650: 50 36 3a 06 00 70 00 00 70 9e 36 1d 01 00 00 00  P6:..p..p.6.....
0x7000063a3660: a8 36 3a 06 00 70 00 00 78 9f 36 1d 01 00 00 00  ?6:..p..x.6.....
0x7000063a3670: 00 00 00 00 00 00 00 00 58 7f bb 47 07 00 00 00  ........X.?G....
0x7000063a3680: 88 9e 36 1d 01 00 00 00 00 00 00 00 00 00 00 00  ..6.............
0x7000063a3690: a8 36 3a 06 00 70 00 00 10 37 3a 06 00 70 00 00  ?6:..p...7:..p..
0x7000063a36a0: f1 09 80 05 01 00 00 00 a0 83 bb 47 07 00 00 00  ?.......?.?G....
0x7000063a36b0: a0 1f 00 00 03 00 00 00 00 00 00 00 00 00 00 00  ?...............
0x7000063a36c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7000063a36d0: 00 00 00 00 00 00 00 00 00 40 3a 06 00 70 00 00  .........@:..p..
0x7000063a36e0: 20 38 3a 06 00 70 00 00 50 3d 3a 06 00 70 00 00   8:..p..P=:..p..
0x7000063a36f0: 0a 00 00 00 00 70 00 00 88 9e 36 1d 01 00 00 00  .....p....6.....
0x7000063a3700: 00 a7 82 05 01 00 00 00 a0 3a 3a 06 00 70 00 00  .?......?::..p..
0x7000063a3710: e0 38 3a 06 00 70 00 00 1d bb 8e 03 01 00 00 00  ?8:..p...?......
0x7000063a3720: 01 00 00 00 00 70 00 00 00 70 80 01 01 00 00 00  .....p...p......
0x7000063a3730: 50 37 3a 06 00 70 00 00 15 95 28 03 01 00 00 00  P7:..p....(.....
0x7000063a3740: 70 37 3a 06 00 70 00 00 00 70 80 01 01 00 00 00  p7:..p...p......
(lldb) p ((Method*)0x0747bb7f58)
(Method *) $2 = 0x0000000747bb7f58
(lldb) p ((Method*)0x011d369e88)->print()
method
 - this oop:          0x000000011d369e88
 - method holder:     'com/hx/test05/Test18InvokeStatic'
 - constants:         0x000000011d369c50 constant pool [23] 0x000000011d369c50 for 'com/hx/test05/Test18InvokeStatic' cache=0x000000011d369f78
 - access:            0x9  public static
 - name:              'main'
 - signature:         '([Ljava/lang/String;)V'
 - max stack:         1
 - max locals:        1
 - size of params:    1
 - method size:       11
 - vtable index:      -2
 - i2i entry:         0x000000010582a700
 - adapters:          AHE@0x00000001010292e0: 0xb0000000 i2c: 0x00000001059a4460 c2i: 0x00000001059a459a c2iUV: 0x00000001059a456d
 - compiled entry     0x00000001059a459a
 - code size:         4
 - code start:        0x000000011d369e70
 - code end (excl):   0x000000011d369e74
 - checked ex length: 0
 - linenumber start:  0x000000011d369e74
 - localvar length:   1
 - localvar start:    0x000000011d369e7a
(lldb) dis
->  0x10584c8bf: movq   %r13, -0x40(%rbp)
    0x10584c8c3: movzwl 0x1(%r13), %edx
    0x10584c8c8: movq   -0x30(%rbp), %rcx
    0x10584c8cc: shll   $0x2, %edx
    0x10584c8cf: movl   0x10(%rcx,%rdx,8), %ebx
    0x10584c8d3: shrl   $0x10, %ebx
    0x10584c8d6: andl   $0xff, %ebx
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8c3
->  0x10584c8c3: movzwl 0x1(%r13), %edx
    0x10584c8c8: movq   -0x30(%rbp), %rcx
    0x10584c8cc: shll   $0x2, %edx
    0x10584c8cf: movl   0x10(%rcx,%rdx,8), %ebx
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8c8
->  0x10584c8c8: movq   -0x30(%rbp), %rcx
    0x10584c8cc: shll   $0x2, %edx
    0x10584c8cf: movl   0x10(%rcx,%rdx,8), %ebx
    0x10584c8d3: shrl   $0x10, %ebx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x00007000063a3650
       rbx = 0x00000000000000b8
       rcx = 0x0000000000000008
       rdx = 0x0000000000000001
       rdi = 0x0000000101807000
       rsi = 0x0000000000000008
       rbp = 0x00007000063a3698
       rsp = 0x00007000063a3650
        r8 = 0x0000000000002901
        r9 = 0x0000000101807000
       r10 = 0x000000010430b270  libjvm.dylib`TemplateInterpreter::_active_table + 18432
       r11 = 0x0000000000000000
       r12 = 0x0000000000000000
       r13 = 0x000000011d369e70
       r14 = 0x00007000063a36a8
       r15 = 0x0000000101807000
       rip = 0x000000010584c8c8
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8cc
->  0x10584c8cc: shll   $0x2, %edx
    0x10584c8cf: movl   0x10(%rcx,%rdx,8), %ebx
    0x10584c8d3: shrl   $0x10, %ebx
    0x10584c8d6: andl   $0xff, %ebx
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8cf
->  0x10584c8cf: movl   0x10(%rcx,%rdx,8), %ebx
    0x10584c8d3: shrl   $0x10, %ebx
    0x10584c8d6: andl   $0xff, %ebx
    0x10584c8dc: cmpl   $0xb8, %ebx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x00007000063a3650
       rbx = 0x00000000000000b8
       rcx = 0x000000011d369f78
       rdx = 0x0000000000000004
       rdi = 0x0000000101807000
       rsi = 0x0000000000000008
       rbp = 0x00007000063a3698
       rsp = 0x00007000063a3650
        r8 = 0x0000000000002901
        r9 = 0x0000000101807000
       r10 = 0x000000010430b270  libjvm.dylib`TemplateInterpreter::_active_table + 18432
       r11 = 0x0000000000000000
       r12 = 0x0000000000000000
       r13 = 0x000000011d369e70
       r14 = 0x00007000063a36a8
       r15 = 0x0000000101807000
       rip = 0x000000010584c8cf
    rflags = 0x0000000000000202
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8d3
->  0x10584c8d3: shrl   $0x10, %ebx
    0x10584c8d6: andl   $0xff, %ebx
    0x10584c8dc: cmpl   $0xb8, %ebx
    0x10584c8e2: je     0x10584cb5c
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8d6
->  0x10584c8d6: andl   $0xff, %ebx
    0x10584c8dc: cmpl   $0xb8, %ebx
    0x10584c8e2: je     0x10584cb5c
    0x10584c8e8: movl   $0xb8, %ebx
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8dc
->  0x10584c8dc: cmpl   $0xb8, %ebx
    0x10584c8e2: je     0x10584cb5c
    0x10584c8e8: movl   $0xb8, %ebx
    0x10584c8ed: callq  0x10584c8f7
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8e2
->  0x10584c8e2: je     0x10584cb5c
    0x10584c8e8: movl   $0xb8, %ebx
    0x10584c8ed: callq  0x10584c8f7
    0x10584c8f2: jmp    0x10584cb50
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584c8e8
->  0x10584c8e8: movl   $0xb8, %ebx
    0x10584c8ed: callq  0x10584c8f7
    0x10584c8f2: jmp    0x10584cb50
    0x10584c8f7: movq   %rbx, %rsi
Target 0: (java) stopped.
(lldb) b 0x10584cb5c
Breakpoint 4: address = 0x000000010584cb5c
(lldb) c
Process 3885 resuming
Process 3885 stopped
* thread #5, stop reason = breakpoint 4.1
    frame #0: 0x000000010584cb5c
->  0x10584cb5c: movq   0x18(%rcx,%rdx,8), %rbx
    0x10584cb61: movl   0x28(%rcx,%rdx,8), %edx
    0x10584cb65: shrl   $0x1c, %edx
    0x10584cb68: movabsq $0x10430c240, %r10        ; imm = 0x10430C240
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584cb61
->  0x10584cb61: movl   0x28(%rcx,%rdx,8), %edx
    0x10584cb65: shrl   $0x1c, %edx
    0x10584cb68: movabsq $0x10430c240, %r10        ; imm = 0x10430C240
    0x10584cb72: movq   (%r10,%rdx,8), %rdx
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584cb65
->  0x10584cb65: shrl   $0x1c, %edx
    0x10584cb68: movabsq $0x10430c240, %r10        ; imm = 0x10430C240
    0x10584cb72: movq   (%r10,%rdx,8), %rdx
    0x10584cb76: pushq  %rdx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000000000000000
       rbx = 0x000000011d369f20
       rcx = 0x000000011d369f78
       rdx = 0x0000000090000000
       rdi = 0x0000000101807000
       rsi = 0x0000000000000008
       rbp = 0x00007000063a3698
       rsp = 0x00007000063a3650
        r8 = 0x0000000000000000
        r9 = 0x0000000114b01290
       r10 = 0x0000000000000000
       r11 = 0x0000000114b0128c
       r12 = 0x0000000000000000
       r13 = 0x000000011d369e70
       r14 = 0x00007000063a36a8
       r15 = 0x0000000101807000
       rip = 0x000000010584cb65
    rflags = 0x0000000000000202
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000
Target 0: (java) stopped.
(lldb) p ((Method*)0x000000011d369f20)->print()
method
 - this oop:          0x000000011d369f20
 - method holder:     'com/hx/test05/Test18InvokeStatic'
 - constants:         0x000000011d369c50 constant pool [23] 0x000000011d369c50 for 'com/hx/test05/Test18InvokeStatic' cache=0x000000011d369f78
 - access:            0x9  public static
 - name:              'foo'
 - signature:         '()V'
 - max stack:         1
 - max locals:        0
 - size of params:    0
 - method size:       11
 - vtable index:      -2
 - i2i entry:         0x000000010582a700
 - adapters:          AHE@0x0000000101029760: 0x i2c: 0x00000001059d4ae0 c2i: 0x00000001059d4c16 c2iUV: 0x00000001059d4be9
 - compiled entry     0x00000001059d4c16
 - code size:         1
 - code start:        0x000000011d369f18
 - code end (excl):   0x000000011d369f19
 - checked ex length: 0
 - linenumber start:  0x000000011d369f19
 - localvar length:   0
(lldb) b 0x10584ccd2
Breakpoint 5: address = 0x000000010584ccd2
(lldb) c
Process 3885 resuming
Process 3885 stopped
* thread #5, stop reason = breakpoint 5.1
    frame #0: 0x000000010584ccd2
->  0x10584ccd2: leaq   0x8(%rsp), %r13
    0x10584ccd7: movq   %r13, -0x10(%rbp)
    0x10584ccdb: jmpq   *0x50(%rbx)
    0x10584ccde: movq   %rsp, -0x28(%rsp)
Target 0: (java) stopped.
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584ccd7
->  0x10584ccd7: movq   %r13, -0x10(%rbp)
    0x10584ccdb: jmpq   *0x50(%rbx)
    0x10584ccde: movq   %rsp, -0x28(%rsp)
    0x10584cce3: subq   $0x80, %rsp
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000000000000000
       rbx = 0x000000011d369f20
       rcx = 0x000000011d369f78
       rdx = 0x000000010580b4a3
       rdi = 0x0000000101807000
       rsi = 0x0000000000000008
       rbp = 0x00007000063a3698
       rsp = 0x00007000063a3648
        r8 = 0x0000000000000000
        r9 = 0x0000000114b01290
       r10 = 0x000000010430c240  libjvm.dylib`TemplateInterpreter::_invoke_return_entry
       r11 = 0x0000000114b0128c
       r12 = 0x0000000000000000
       r13 = 0x00007000063a3650
       r14 = 0x00007000063a36a8
       r15 = 0x0000000101807000
       rip = 0x000000010584ccd7
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010584ccdb
->  0x10584ccdb: jmpq   *0x50(%rbx)
    0x10584ccde: movq   %rsp, -0x28(%rsp)
    0x10584cce3: subq   $0x80, %rsp
    0x10584ccea: movq   %rax, 0x78(%rsp)
Target 0: (java) stopped.
(lldb) x 0x00007000063a3648 -c 0x100
0x7000063a3648: a3 b4 80 05 01 00 00 00 50 36 3a 06 00 70 00 00  ??......P6:..p..
0x7000063a3658: 70 9e 36 1d 01 00 00 00 a8 36 3a 06 00 70 00 00  p.6.....?6:..p..
0x7000063a3668: 78 9f 36 1d 01 00 00 00 00 00 00 00 00 00 00 00  x.6.............
0x7000063a3678: 58 7f bb 47 07 00 00 00 88 9e 36 1d 01 00 00 00  X.?G......6.....
0x7000063a3688: 50 36 3a 06 00 70 00 00 a8 36 3a 06 00 70 00 00  P6:..p..?6:..p..
0x7000063a3698: 10 37 3a 06 00 70 00 00 f1 09 80 05 01 00 00 00  .7:..p..?.......
0x7000063a36a8: a0 83 bb 47 07 00 00 00 a0 1f 00 00 03 00 00 00  ?.?G....?.......
0x7000063a36b8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7000063a36c8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x7000063a36d8: 00 40 3a 06 00 70 00 00 20 38 3a 06 00 70 00 00  .@:..p.. 8:..p..
0x7000063a36e8: 50 3d 3a 06 00 70 00 00 0a 00 00 00 00 70 00 00  P=:..p.......p..
0x7000063a36f8: 88 9e 36 1d 01 00 00 00 00 a7 82 05 01 00 00 00  ..6......?......
0x7000063a3708: a0 3a 3a 06 00 70 00 00 e0 38 3a 06 00 70 00 00  ?::..p..?8:..p..
0x7000063a3718: 1d bb 8e 03 01 00 00 00 01 00 00 00 00 70 00 00  .?...........p..
0x7000063a3728: 00 70 80 01 01 00 00 00 50 37 3a 06 00 70 00 00  .p......P7:..p..
0x7000063a3738: 15 95 28 03 01 00 00 00 70 37 3a 06 00 70 00 00  ..(.....p7:..p..
(lldb) nexti
Process 3885 stopped
* thread #5, stop reason = instruction step over
    frame #0: 0x000000010582a700
->  0x10582a700: movq   0x10(%rbx), %rdx
    0x10582a704: movzwl 0x34(%rdx), %ecx
    0x10582a708: movzwl 0x32(%rdx), %edx
    0x10582a70c: subl   %ecx, %edx
Target 0: (java) stopped.
(lldb)

判断了一下 方法是否已经解析完成, 如果没有解析 则调用 InterpreterRuntime::resolve_from_cache 触发解析

然后 之后更新 rbx 为方法的 解析的目标地址, 然后 跳到给定的方法的 entry_point

entry_point 部分的调用代码 可以参考 15 main方法的栈帧信息 

当然 其中还有一些 处理标志, 处理返回地址, profiling 方法的调用信息 等等 

invokestatic 的汇编?

invokestatic 的汇编入口主要来自于 templateTable_x86.cpp invokestatic 

templateTable_x86.cpp invokestatic

void TemplateTable::invokestatic(int byte_no) 
  transition(vtos, vtos);
  assert(byte_no == f1_byte, "use this argument");
  prepare_invoke(byte_no, rbx);  // get f1 Method*
  // do the call
  __ profile_call(rax);
  __ profile_arguments_type(rax, rbx, rbcp, false);
  __ jump_from_interpreted(rbx, rax);

templateTable_x86.cpp prepare_invoke 

void TemplateTable::prepare_invoke(int byte_no,
                                   Register method,  // linked method (or i-klass)
                                   Register index,   // itable index, MethodType, etc.
                                   Register recv,    // if caller wants to see it
                                   Register flags    // if caller wants to test it
                                   ) 
  // determine flags
  const Bytecodes::Code code = bytecode();
  const bool is_invokeinterface  = code == Bytecodes::_invokeinterface;
  const bool is_invokedynamic    = code == Bytecodes::_invokedynamic;
  const bool is_invokehandle     = code == Bytecodes::_invokehandle;
  const bool is_invokevirtual    = code == Bytecodes::_invokevirtual;
  const bool is_invokespecial    = code == Bytecodes::_invokespecial;
  const bool load_receiver       = (recv  != noreg);
  const bool save_flags          = (flags != noreg);
  assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), "");
  assert(save_flags    == (is_invokeinterface || is_invokevirtual), "need flags for vfinal");
  assert(flags == noreg || flags == rdx, "");
  assert(recv  == noreg || recv  == rcx, "");

  // setup registers & access constant pool cache
  if (recv  == noreg)  recv  = rcx;
  if (flags == noreg)  flags = rdx;
  assert_different_registers(method, index, recv, flags);

  // save 'interpreter return address'
  __ save_bcp();

  load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);

  // maybe push appendix to arguments (just before return address)
  if (is_invokedynamic || is_invokehandle) 
    Label L_no_push;
    __ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift));
    __ jcc(Assembler::zero, L_no_push);
    // Push the appendix as a trailing parameter.
    // This must be done before we get the receiver,
    // since the parameter_size includes it.
    __ push(rbx);
    __ mov(rbx, index);
    assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0");
    __ load_resolved_reference_at_index(index, rbx);
    __ pop(rbx);
    __ push(index);  // push appendix (MethodType, CallSite, etc.)
    __ bind(L_no_push);
  

  // load receiver if needed (after appendix is pushed so parameter size is correct)
  // Note: no return address pushed yet
  if (load_receiver) 
    __ movl(recv, flags);
    __ andl(recv, ConstantPoolCacheEntry::parameter_size_mask);
    const int no_return_pc_pushed_yet = -1;  // argument slot correction before we push return address
    const int receiver_is_at_end      = -1;  // back off one slot to get receiver
    Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end);
    __ movptr(recv, recv_addr);
    __ verify_oop(recv);
  

  if (save_flags) 
    __ movl(rbcp, flags);
  

  // compute return type
  __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
  // Make sure we don't need to mask flags after the above shift
  ConstantPoolCacheEntry::verify_tos_state_shift();
  // load return address
  
    const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code);
    ExternalAddress table(table_addr);
    LP64_ONLY(__ lea(rscratch1, table));
    LP64_ONLY(__ movptr(flags, Address(rscratch1, flags, Address::times_ptr)));
    NOT_LP64(__ movptr(flags, ArrayAddress(table, Address(noreg, flags, Address::times_ptr))));
  

  // push return address
  __ push(flags);

  // Restore flags value from the constant pool cache, and restore rsi
  // for later null checks.  r13 is the bytecode pointer
  if (save_flags) 
    __ movl(flags, rbcp);
    __ restore_bcp();
  

templateTable_x86.cpp load_invoke_cp_cache_entry

void TemplateTable::load_invoke_cp_cache_entry(int byte_no,
                                               Register method,
                                               Register itable_index,
                                               Register flags,
                                               bool is_invokevirtual,
                                               bool is_invokevfinal, /*unused*/
                                               bool is_invokedynamic) 
  // setup registers
  const Register cache = rcx;
  const Register index = rdx;
  assert_different_registers(method, flags);
  assert_different_registers(method, cache, index);
  assert_different_registers(itable_index, flags);
  assert_different_registers(itable_index, cache, index);
  // determine constant pool cache field offsets
  assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
  const int method_offset = in_bytes(
    ConstantPoolCache::base_offset() +
      ((byte_no == f2_byte)
       ? ConstantPoolCacheEntry::f2_offset()
       : ConstantPoolCacheEntry::f1_offset()));
  const int flags_offset = in_bytes(ConstantPoolCache::base_offset() +
                                    ConstantPoolCacheEntry::flags_offset());
  // access constant pool cache fields
  const int index_offset = in_bytes(ConstantPoolCache::base_offset() +
                                    ConstantPoolCacheEntry::f2_offset());

  size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
  resolve_cache_and_index(byte_no, cache, index, index_size);
    __ movptr(method, Address(cache, index, Address::times_ptr, method_offset));

  if (itable_index != noreg) 
    // pick up itable or appendix index from f2 also:
    __ movptr(itable_index, Address(cache, index, Address::times_ptr, index_offset));
  
  __ movl(flags, Address(cache, index, Address::times_ptr, flags_offset));

templateTable_x86.cpp resolve_cache_and_index

void TemplateTable::resolve_cache_and_index(int byte_no,
                                            Register Rcache,
                                            Register index,
                                            size_t index_size) 
  const Register temp = rbx;
  assert_different_registers(Rcache, index, temp);

  Label resolved;

  Bytecodes::Code code = bytecode();
  switch (code) 
  case Bytecodes::_nofast_getfield: code = Bytecodes::_getfield; break;
  case Bytecodes::_nofast_putfield: code = Bytecodes::_putfield; break;
  

  assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range");
  __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
  __ cmpl(temp, code);  // have we resolved this bytecode?
  __ jcc(Assembler::equal, resolved);

  // resolve first time through
  address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
  __ movl(temp, code);
  __ call_VM(noreg, entry, temp);
  // Update registers with resolved info
  __ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
  __ bind(resolved);

对于 invokestatic 生成出来的汇编如下 

(lldb) dis -s 0x000000011f4178a0 -c 0x100
	// set_vtos_entry_points 的前置处理
    0x11f4178a0: pushq  %rax
    0x11f4178a1: jmp    0x11f4178df
    0x11f4178a6: subq   $0x8, %rsp
    0x11f4178aa: vmovss %xmm0, (%rsp)
    0x11f4178af: jmp    0x11f4178df
    0x11f4178b4: subq   $0x10, %rsp
    0x11f4178b8: vmovsd %xmm0, (%rsp)
    0x11f4178bd: jmp    0x11f4178df
    0x11f4178c2: subq   $0x10, %rsp
    0x11f4178c6: movq   %rax, (%rsp)
    0x11f4178ca: movabsq $0x0, %r10
    0x11f4178d4: movq   %r10, 0x8(%rsp)
    0x11f4178d9: jmp    0x11f4178df
    0x11f4178de: pushq  %rax

    // save_bcp
    0x11f4178df: movq   %r13, -0x40(%rbp)
    // get_cache_and_index_and_bytecode_at_bcp
    // get_cache_and_index_at_bcp
    // constantsPoolCache = -0x30(%rbp)
    // index = 0x1(%r13) << 2 // 基于 constantsPoolCache.base() 的偏移
    0x11f4178e3: movzwl 0x1(%r13), %edx
    0x11f4178e8: movq   -0x30(%rbp), %rcx
    0x11f4178ec: shll   $0x2, %edx

	// if(cache_entry(thread).is_resolved(invokestatic)) goto 0x11f417b7c
	// +0x10, 跳过 constantsPoolCache 头部 16byte
    0x11f4178ef: movl   0x10(%rcx,%rdx,8), %ebx
    0x11f4178f3: shrl   $0x10, %ebx
    0x11f4178f6: andl   $0xff, %ebx

	// have we resolved this bytecode?
    0x11f4178fc: cmpl   $0xb8, %ebx
    0x11f417902: je     0x11f417b7c

    // InterpreterRuntime::resolve_from_cache
    0x11f417908: movl   $0xb8, %ebx
    0x11f41790d: callq  0x11f417917
    0x11f417912: jmp    0x11f417b70
    0x11f417917: movq   %rbx, %rsi
    0x11f41791a: leaq   0x8(%rsp), %rax
    0x11f41791f: movq   %r13, -0x40(%rbp)
    0x11f417923: cmpq   $0x0, -0x10(%rbp)
    0x11f41792b: je     0x11f4179a8
    0x11f417931: movq   %rsp, -0x28(%rsp)
    0x11f417936: subq   $0x80, %rsp
    0x11f41793d: movq   %rax, 0x78(%rsp)
    0x11f417942: movq   %rcx, 0x70(%rsp)
    0x11f417947: movq   %rdx, 0x68(%rsp)
    0x11f41794c: movq   %rbx, 0x60(%rsp)
    0x11f417951: movq   %rbp, 0x50(%rsp)
    0x11f417956: movq   %rsi, 0x48(%rsp)
    0x11f41795b: movq   %rdi, 0x40(%rsp)
    0x11f417960: movq   %r8, 0x38(%rsp)
    0x11f417965: movq   %r9, 0x30(%rsp)
    0x11f41796a: movq   %r10, 0x28(%rsp)
    0x11f41796f: movq   %r11, 0x20(%rsp)
    0x11f417974: movq   %r12, 0x18(%rsp)
    0x11f417979: movq   %r13, 0x10(%rsp)
    0x11f41797e: movq   %r14, 0x8(%rsp)
    0x11f417983: movq   %r15, (%rsp)
    0x11f417987: movabsq $0x1116984ed, %rdi        ; imm = 0x1116984ED
    0x11f417991: movabsq $0x11f417931, %rsi        ; imm = 0x11F417931
    0x11f41799b: movq   %rsp, %rdx
    0x11f41799e: andq   $-0x10, %rsp
    0x11f4179a2: callq  0x11116af60               ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
    0x11f4179a7: hlt
    0x11f4179a8: pushq  %r10
    0x11f4179aa: cmpq   -0xdb013c1(%rip), %r12    ; Universe::_narrow_ptrs_base
    0x11f4179b1: je     0x11f417a2e
    0x11f4179b7: movq   %rsp, -0x28(%rsp)
    0x11f4179bc: subq   $0x80, %rsp
    0x11f4179c3: movq   %rax, 0x78(%rsp)
    0x11f4179c8: movq   %rcx, 0x70(%rsp)
    0x11f4179cd: movq   %rdx, 0x68(%rsp)
    0x11f4179d2: movq   %rbx, 0x60(%rsp)
    0x11f4179d7: movq   %rbp, 0x50(%rsp)
    0x11f4179dc: movq   %rsi, 0x48(%rsp)
    0x11f4179e1: movq   %rdi, 0x40(%rsp)
    0x11f4179e6: movq   %r8, 0x38(%rsp)
    0x11f4179eb: movq   %r9, 0x30(%rsp)
    0x11f4179f0: movq   %r10, 0x28(%rsp)
    0x11f4179f5: movq   %r11, 0x20(%rsp)
    0x11f4179fa: movq   %r12, 0x18(%rsp)
    0x11f4179ff: movq   %r13, 0x10(%rsp)
    0x11f417a04: movq   %r14, 0x8(%rsp)
    0x11f417a09: movq   %r15, (%rsp)
    0x11f417a0d: movabsq $0x1116d4efc, %rdi        ; imm = 0x1116D4EFC
    0x11f417a17: movabsq $0x11f4179b7, %rsi        ; imm = 0x11F4179B7
    0x11f417a21: movq   %rsp, %rdx
    0x11f417a24: andq   $-0x10, %rsp
    0x11f417a28: callq  0x11116af60               ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
    0x11f417a2d: hlt
    0x11f417a2e: popq   %r10
    0x11f417a30: movq   %r15, %rdi
    0x11f417a33: movq   %rbp, 0x218(%r15)
    0x11f417a3a: movq   %rax, 0x208(%r15)
    0x11f417a41: testl  $0xf, %esp
    0x11f417a47: je     0x11f417a5f
    0x11f417a4d: subq   $0x8, %rsp
    0x11f417a51: callq  0x110ed41a0               ; InterpreterRuntime::resolve_from_cache at interpreterRuntime.cpp:868
    0x11f417a56: addq   $0x8, %rsp
    0x11f417a5a: jmp    0x11f417a64
    0x11f417a5f: callq  0x110ed41a0               ; InterpreterRuntime::resolve_from_cache at interpreterRuntime.cpp:868
    0x11f417a64: pushq  %rax
    0x11f417a65: pushq  %rdi
    0x11f417a66: pushq  %rsi
    0x11f417a67: pushq  %rdx
    0x11f417a68: pushq  %rcx
    0x11f417a69: pushq  %r8
    0x11f417a6b: pushq  %r9
    0x11f417a6d: pushq  %r10
    0x11f417a6f: pushq  %r11
    0x11f417a71: testl  $0xf, %esp
    0x11f417a77: je     0x11f417a8f
    0x11f417a7d: subq   $0x8, %rsp
    0x11f417a81: callq  0x110603ae0               ; Thread::current at thread.hpp:660
    0x11f417a86: addq   $0x8, %rsp
    0x11f417a8a: jmp    0x11f417a94
    0x11f417a8f: callq  0x110603ae0               ; Thread::current at thread.hpp:660
    0x11f417a94: popq   %r11
    0x11f417a96: popq   %r10
    0x11f417a98: popq   %r9
    0x11f417a9a: popq   %r8
    0x11f417a9c: popq   %rcx
    0x11f417a9d: popq   %rdx
    0x11f417a9e: popq   %rsi
    0x11f417a9f: popq   %rdi
    0x11f417aa0: cmpq   %rax, %r15
    0x11f417aa3: je     0x11f417b20
    0x11f417aa9: movq   %rsp, -0x28(%rsp)
    0x11f417aae: subq   $0x80, %rsp
    0x11f417ab5: movq   %rax, 0x78(%rsp)
    0x11f417aba: movq   %rcx, 0x70(%rsp)
    0x11f417abf: movq   %rdx, 0x68(%rsp)
    0x11f417ac4: movq   %rbx, 0x60(%rsp)
    0x11f417ac9: movq   %rbp, 0x50(%rsp)
    0x11f417ace: movq   %rsi, 0x48(%rsp)
    0x11f417ad3: movq   %rdi, 0x40(%rsp)
    0x11f417ad8: movq   %r8, 0x38(%rsp)
    0x11f417add: movq   %r9, 0x30(%rsp)
    0x11f417ae2: movq   %r10, 0x28(%rsp)
    0x11f417ae7: movq   %r11, 0x20(%rsp)
    0x11f417aec: movq   %r12, 0x18(%rsp)
    0x11f417af1: movq   %r13, 0x10(%rsp)
    0x11f417af6: movq   %r14, 0x8(%rsp)
    0x11f417afb: movq   %r15, (%rsp)
    0x11f417aff: movabsq $0x1116d5043, %rdi        ; imm = 0x1116D5043
    0x11f417b09: movabsq $0x11f417aa9, %rsi        ; imm = 0x11F417AA9
    0x11f417b13: movq   %rsp, %rdx
    0x11f417b16: andq   $-0x10, %rsp
    0x11f417b1a: callq  0x11116af60               ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
    0x11f417b1f: hlt
    0x11f417b20: popq   %rax
    0x11f417b21: movabsq $0x0, %r10
    0x11f417b2b: movq   %r10, 0x208(%r15)
    0x11f417b32: movabsq $0x0, %r10
    0x11f417b3c: movq   %r10, 0x218(%r15)
    0x11f417b43: movabsq $0x0, %r10
    0x11f417b4d: movq   %r10, 0x210(%r15)
    0x11f417b54: cmpq   $0x0, 0x8(%r15)
    0x11f417b5c: je     0x11f417b67
    0x11f417b62: jmp    0x11f3d77a0
    0x11f417b67: movq   -0x40(%rbp), %r13
    0x11f417b6b: movq   -0x38(%rbp), %r14
    0x11f417b6f: retq

	// get_cache_and_index_at_bcp
    0x11f417b70: movzwl 0x1(%r13), %edx
    0x11f417b75: movq   -0x30(%rbp), %rcx
    0x11f417b79: shll   $0x2, %edx

    // rbx = method, rdx = flags
    0x11f417b7c: movq   0x18(%rcx,%rdx,8), %rbx
    0x11f417b81: movl   0x28(%rcx,%rdx,8), %edx

	// compute return type
    0x11f417b85: shrl   $0x1c, %edx
    // load return address
    0x11f417b88: movabsq $0x11190c240, %r10        ; imm = 0x11190C240
    0x11f417b92: movq   (%r10,%rdx,8), %rdx
    // push return address
    0x11f417b96: pushq  %rdx

    // profile_call
    0x11f417b97: movq   -0x28(%rbp), %rax
    0x11f417b9b: testq  %rax, %rax
    0x11f417b9e: je     0x11f417bb6
    0x11f417ba4: addq   $0x1, 0x8(%rax)
    0x11f417ba9: sbbq   $0x0, 0x8(%rax)
    0x11f417bae: addq   $0x10, %rax
    0x11f417bb2: movq   %rax, -0x28(%rbp)

    // profile_arguments_type
    0x11f417bb6: movq   -0x28(%rbp), %rax
    0x11f417bba: testq  %rax, %rax
    0x11f417bbd: je     0x11f417cf2
    0x11f417bc3: cmpb   $0xa, -0x10(%rax)
    0x11f417bc7: jne    0x11f417cf2
    0x11f417bcd: addq   $0x8, %rax
    0x11f417bd1: movq   -0x8(%rax), %r13
    0x11f417bd5: subl   $0x0, %r13d
    0x11f417bd9: cmpl   $0x2, %r13d
    0x11f417bdd: jl     0x11f417ce7
    0x11f417be3: movq   0x10(%rbx), %r13
    0x11f417be7: movzwl 0x34(%r13), %r13d
    0x11f417bec: subq   (%rax), %r13
    0x11f417bef: subl   $0x1, %r13d
    0x11f417bf3: movq   0x8(%rsp,%r13,8), %r13
    0x11f417bf8: testq  %r13, %r13
    0x11f417bfb: jne    0x11f417c07
    0x11f417bfd: orq    $0x1, 0x8(%rax)
    0x11f417c05: jmp    0x11f417c54
    0x11f417c07: movl   0x8(%r13), %r13d
    0x11f417c0b: shlq   $0x3, %r13
    0x11f417c0f: xorq   0x8(%rax), %r13
    0x11f417c13: testq  $-0x4, %r13
    0x11f417c1a: je     0x11f417c54
    0x11f417c1c: testq  $0x2, %r13
    0x11f417c23: jne    0x11f417c54
    0x11f417c25: cmpq   $0x0, 0x8(%rax)
    0x11f417c2d: je     0x11f417c50
    0x11f417c2f: cmpq   $0x1, 0x8(%rax)
    0x11f417c37: je     0x11f417c50
    0x11f417c39: xorq   0x8(%rax), %r13
    0x11f417c3d: testq  $-0x4, %r13
    0x11f417c44: je     0x11f417c54
    0x11f417c46: orq    $0x2, 0x8(%rax)
    0x11f417c4e: jmp    0x11f417c54
    0x11f417c50: movq   %r13, 0x8(%rax)
    0x11f417c54: addq   $0x10, %rax
    0x11f417c58: movq   -0x18(%rax), %r13
    0x11f417c5c: subl   $0x2, %r13d
    0x11f417c60: cmpl   $0x2, %r13d
    0x11f417c64: jl     0x11f417ce7
    0x11f417c6a: movq   0x10(%rbx), %r13
    0x11f417c6e: movzwl 0x34(%r13), %r13d
    0x11f417c73: subq   (%rax), %r13
    0x11f417c76: subl   $0x1, %r13d
    0x11f417c7a: movq   0x8(%rsp,%r13,8), %r13
    0x11f417c7f: testq  %r13, %r13
    0x11f417c82: jne    0x11f417c8e
    0x11f417c84: orq    $0x1, 0x8(%rax)
    0x11f417c8c: jmp    0x11f417cdb
    0x11f417c8e: movl   0x8(%r13), %r13d
    0x11f417c92: shlq   $0x3, %r13
    0x11f417c96: xorq   0x8(%rax), %r13
    0x11f417c9a: testq  $-0x4, %r13
    0x11f417ca1: je     0x11f417cdb
    0x11f417ca3: testq  $0x2, %r13
    0x11f417caa: jne    0x11f417cdb
    0x11f417cac: cmpq   $0x0, 0x8(%rax)
    0x11f417cb4: je     0x11f417cd7
    0x11f417cb6: cmpq   $0x1, 0x8(%rax)
    0x11f417cbe: je     0x11f417cd7
    0x11f417cc0: xorq   0x8(%rax), %r13
    0x11f417cc4: testq  $-0x4, %r13
    0x11f417ccb: je     0x11f417cdb
    0x11f417ccd: orq    $0x2, 0x8(%rax)
    0x11f417cd5: jmp    0x11f417cdb
    0x11f417cd7: movq   %r13, 0x8(%rax)
    0x11f417cdb: addq   $0x10, %rax
    0x11f417cdf: movq   -0x28(%rax), %r13
    0x11f417ce3: subl   $0x4, %r13d
    0x11f417ce7: shll   $0x3, %r13d
    0x11f417ceb: addq   %r13, %rax
    0x11f417cee: movq   %rax, -0x28(%rbp)

    // jump_from_interpreted
    0x11f417cf2: leaq   0x8(%rsp), %r13
    0x11f417cf7: movq   %r13, -0x10(%rbp)
    0x11f417cfb: jmpq   *0x50(%rbx)
    0x11f417cfe: int3
    0x11f417cff: int3
    0x11f417d00: int3
    0x11f417d01: int3
    0x11f417d02: int3
    0x11f417d03: int3
    0x11f417d04: int3
    0x11f417d05: int3
    0x11f417d06: int3
    0x11f417d07: int3
    0x11f417d08: int3

InterpreterRuntime::resolve_from_cache 的处理

这里面主要是 类, 方法 的相关 符号引用 替换为 直接引用, 以及 相关标记 

加载方法的时候 会做一系列的校验处理 

这部分的内容 暂时就到这里吧 

添加于2020.07.04 - bytecodeInterpreter.cpp 中的逻辑代码

bytecodeInterpreter.cpp 中可读性更好的代码 

      CASE(_invokevirtual):
      CASE(_invokespecial):
      CASE(_invokestatic): 
        u2 index = Bytes::get_native_u2(pc+1);

        ConstantPoolCacheEntry* cache = cp->entry_at(index);
        // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases
        // out so c++ compiler has a chance for constant prop to fold everything possible away.

        if (!cache->is_resolved((Bytecodes::Code)opcode)) 
          CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode),
                  handle_exception);
          cache = cp->entry_at(index);
        

        istate->set_msg(call_method);
        
          Method* callee;
          // 省略部分 invokevirtual, invokespecial 的处理 
          istate->set_callee(callee);
          istate->set_callee_entry_point(callee->from_interpreted_entry());
#ifdef VM_JVMTI
          if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) 
            istate->set_callee_entry_point(callee->interpreter_entry());
          
#endif /* VM_JVMTI */
          istate->set_bcp_advance(3);
          UPDATE_PC_AND_RETURN(0); // I'll be back...
        
      

完 

参考

java 反射调用 private 相关

14 HelloWorld的字节码的编译执行的调试

15 main方法的栈帧信息

07 运行时常量池索引的 rewrite

以上是关于23 方法调用的流程(invokestatic为例)的主要内容,如果未能解决你的问题,请参考以下文章

[inside hotspot] java方法调用的StubCode

04 JVM是如何执行方法调用的(下)

Java虚拟机 对象创建流程初始化流程

jvm是怎样调用方法的

JVM方法调用

JVM理论:(三/4)方法调用