Hotspot JVM 底层C/C++ 源码 入门--2方法调用

先看一段代码

public class first{
    public static void main(String[] args){
        add(5,8);
    }
    public static int add(int a, int b){
        int c = a + b;
        int d = c + 9;
        return d;
    }
}

javac     后再用    javap -verbose first

{

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: iconst_5    //看到个5,压栈
         1: bipush        8 //看到个8
         3: invokestatic  #2  // 调用Method add:(II)I
         6: pop
         7: return

  public static int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=2
         0: iload_0 //栈读取变量
         1: iload_1 //两个
         2: iadd    //明显add
         3: istore_2
         4: iload_2
         5: bipush        9
         7: iadd
         8: istore_3  //返回值压栈
         9: iload_3
        10: ireturn
      LineNumberTable:
        line 6: 0
        line 7: 4
        line 8: 9
}
SourceFile: "first.java"

java源文件经过javac后转变为字节码文件,而字节码文件逻辑很容易看懂(关键是一个全局)

看看实际运行,字节码经过jvm转换为机器码(iload_0,iload_1,iadd等)

等价于前一章所说,注意,code[]由jvm动态生成

这就是JVM内部能够直接由C程序调用和执行机器指令的奥秘!而在JVM内部这个函数指针就是call_stub.

call_stub函数指针原型定义在stubRoutines.hpp中

/src/share/vm/runtime/stubRoutines.hpp(码云上搜索hotspot openjdk就有了)

static CallStub call_stub()   
{ 
    return CAST_TO_FN_PTR(CallStub, _call_stub_entry); 
}

在JVM中,凡事出现函数名大写的情况,基本都是宏

#define CAST_TO_FN_PTR(func_type, value) ((func_type)(castable_address(value)))

call_stub()即是如下

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

CallStub是一个函数指针类型,返回值为void,参数有8个

void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) {
  //.............
  // do call
  { JavaCallWrapper link(method, receiver, result, CHECK);
    { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner

      StubRoutines::call_stub()(       //通过call_stub()返回函数指针再传入参数进行调用
        (address)&link,
        // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
        result_val_address,          // see NOTE above (compiler problem)
        result_type,
        method(),
        entry_point,
        args->parameters(),
        args->size_of_parameters(),
        CHECK
      );

      result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
      // Preserve oop return value across possible gc points
      if (oop_result_flag) {
        thread->set_vm_result((oop) result->get_jobject());
      }
    }
  } // Exit JavaCallWrapper (can block - potential return oop must be preserved)

  //...........
}

call_stub()内部使用的castable_address()

inline address_word  castable_address(address x)              { return address_word(x) ; }
inline address_word  castable_address(void* x)                { return address_word(x) ; }
typedef uintptr_t     address_word; // unsigned integer which will hold a pointer
                                    // except for some implementations of a C++
                                    // linkage pointer to function. Should never
                                    // need one of those to be placed in this
                                    // type anyway.

在linux平台上uintptr_t定义如下

typedef unsigned int uintptr_t;

也就是说

static CallStub call_stub(){
    return (CallStub)(unsigned int(_call_stub_entry));
}
static address _call_stub_entry;

将_call_stub_entry转换为(unsigned int) 后再转换为(CallStub函数指针类型)

所以调用函数的实质在于将_call_stub_entry这个变量指向某个char code[]区域(后面会介绍)

下面简要介绍CallStub参数

link:

class JavaCallWrapper: StackObj {
  friend class VMStructs;
 private:
  JavaThread*      _thread;                 // the thread to which this call belongs
  JNIHandleBlock*  _handles;                // the saved handle block
  Method*          _callee_method;          // to be able to collect arguments if entry frame is top frame
  oop              _receiver;               // the receiver of the call (if a non-static call)

  JavaFrameAnchor  _anchor;                 // last thread anchor state that we must restore

  JavaValue*       _result;                 // result value

 public:
  // Construction/destruction
   JavaCallWrapper(methodHandle callee_method, Handle receiver, JavaValue* result, TRAPS);
  ~JavaCallWrapper();

  // Accessors
  JavaThread*      thread() const           { return _thread; }
  JNIHandleBlock*  handles() const          { return _handles; }

  JavaFrameAnchor* anchor(void)             { return &_anchor; }

  JavaValue*       result() const           { return _result; }
  // GC support
  Method*          callee_method()          { return _callee_method; }
  oop              receiver()               { return _receiver; }
  void             oops_do(OopClosure* f);

  bool             is_first_frame() const   { return _anchor.last_Java_sp() == NULL; }

};

大概就是_thread当前Java函数所在线程,_handles本地调用句柄_anchorJava线程的堆栈对象,

可以理解为被调用函数的上下文环境

method:

一切皆对象.每一个Java方法在被JVM加载时,JVM都会在内部为Java方法建立模型,类似Java里面的Method对象

  • 函数名称,所属的类
  • 参数信息
  • 字节码信息
  • ...

JVM在调用CallStub()函数指针式,将method()对象传递进去,最终就是为了从method()对象中获取到java函数的字节码信息,

而拿到字节码后便开始对字节码进行解析执行了.

entry_point

前面分析JVM通过_call_stub_entry所指向的地址,调用Java函数

在JVM通过_call_stub_entry调用Java方法之前,必须要先经过entry_point例程设置code地址(Maybe)

parameters

这个参数看名字就知道什么意思.用来描述Java方法的参数信息.在JVM为java方法分配堆栈式会将参数逐个入栈.一般会保存指针(引用)

size_of_parameters()

参数数量,即parameter个数

 

下一章_call_stub_entry例程

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值