揭秘java虚拟机(一)虚拟机

虚拟机

要让CPU执行一段代码,只需将CS:IP段寄存器指向代码入口处即可.
CS寄存器保存段地址,IP保存偏移地址

常见汇编

数据传输

  • mov1
//将自然数1放入eax寄存器
mov1 1 %eax
  • pop
//将栈顶数据弹出至eax寄存器
pop %eax

算术运算指令

  • add
// 将自然数3与eax寄存器中的数累加,将结果存储进eax
add 3 , %eax
  • inc
// 将ebx寄存器中数增1
inc %ebx

逻辑运算指令

  • sh1
//将eax中的数左移一个二进位
sh1 %eax,1
  • and
//对a1寄存器中数和操作数进行与操作
and a1,00111011B

串指令

程序转移指令

JVM指令

数据交换

函数调用

运算指令

控制转移

对象创建&类型转换

方法调用

汇编

保存栈基并分配新栈

// 保存调用者(main的调用者是OS)的栈基地址
push1 %ebp
//将调用者的栈基地址指向其栈顶
mov1 %esp,%ebp
//分配栈空间,sub1表示减(因为栈是从大向小)
sub1 $32,%esp

运行结果(64位)如下图
在这里插入图片描述
后面可以发现其实栈分配的有多的,但是为了对齐(无论32位还是64位,都需要是64字节整数倍)

初始化数据

//将数字5和3分别放入栈中,20和24表示偏移量.另外28(%esp)是预留给方法返回值
mov1 $5,20(%esp)
mov1 $3,24(%esp)

在这里插入图片描述

压栈

// 借助%eax将两个数(被调用方法的入参)放入栈顶
mov1 24(%esp),%eax
mov1 %eax,4(%esp)
mov1 20(%esp),%eax
mov1 %eax,(%esp)

效果如下图
在这里插入图片描述

调用

calladd
//将add函数返回的结果(默认在%eax中)放入自己的返回值存放处(28(%esp))
mov1 %eax,28(%esp)

返回

mov1 $0,%eax
leave
ret

eip & ebp

被调用的add代码如下

add:
	push1 %ebp
	mov1 %esp,%ebp
	sub1 $16,%esp
	// 从main的栈顶获取入参
	mov1 12(%ebp),%eax
	mov1 8(%ebp),%edx
	// 执行运算
	add1 %edx,%eax
	mov1 %eax,-4(%ebp)
	//返回
	mov1 -4(%ebp),%eax
	leave
	ret

运行时栈如图所示
在这里插入图片描述
eip用来存放调用后返回时调用函数应该执行的指令的地址(本例中是calladd下面一条指令的地址),这个是由calladd这条指令由物理机器自动完成的,这样就知道add函数调用完之后main函数从哪儿执行
ebp是由被调用函数的第一行推入的
leave其实它相当于以下指令

mov %ebp,%esp
pop %eip

C

对于linux平台而言,调用者函数向被调用者函数传递参数时,采用逆向顺序压栈,及最后一个参数第一个压栈,而第一个参数最后一个压栈

函数指针

两种定义方式

  • 直接声明

    return_type (*func_pointer)(date_type1 arg1,date_type2 arg2...data_typen argn);
    
  • 通过类型声明

    typedef (*func_pointer_type)(date_type1 arg1,data_type2,arg2...date_typen argn);
    

    采用下面一种方式只是声明了函数指针,如果想初始化(实例化)一个函数指针还需要执行下列语句

    func_pointer_type func_pointer
    

两种调用方式

  • (*funcPointer)(参数列表)
  • funcPointer(参数列表)

采用第二种方式就和普通的函数调用一致,所有写成第一种可以更明确

CallStub

CAST_TO_FN_PTR

按照globalDefinitions.hpp中的定义如下,这个看上去是按照cpp的语法,和书中的c有所区别

// ANSI C++ does not allow casting from one pointer type to a function pointer
// directly without at best a warning. This macro accomplishes it silently
// In every case that is present at this point the value be cast is a pointer
// to a C linkage function. In some case the type used for the cast reflects
// that linkage and a picky compiler would not complain. In other cases because
// there is no convenient place to place a typedef with extern C linkage (i.e
// a platform dependent header file) it doesn't. At this point no compiler seems
// picky enough to catch these instances (which are few). It is possible that
// using templates could fix these for all cases. This use of templates is likely
// so far from the middle of the road that it is likely to be problematic in
// many C++ compilers.
//
#define CAST_TO_FN_PTR(func_type, value) (reinterpret_cast<func_type>(value))
#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr)))

上面的reinterpret_cast是C++的关键字,可以看做C++的强制转换

call_stub

按照stubRoutines.hpp中的定义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 CAST_TO_FN_PTR(CallStub, _call_stub_entry); }

call_stub调用的地方是javaCalls.cpp的,这个方法很长,有各种if宏判断,这里通过调用call_stub()得到一个需要八个参数的函数并调用
其中八个参数的含义如下:

  • link
    连接器
  • result
    函数返回值地址
  • result_type
    函数返回类型
  • mothod
    jvm内部所表示的Java方法对象
  • entry_point
    java方法例程入口
  • parameters
    java方法的入参集合
  • size_of_parameters
    入参数量
  • TRAPS
    当前线程对象
// do call
  { JavaCallWrapper link(method, receiver, result, CHECK);
    { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner

      // NOTE: if we move the computation of the result_val_address inside
      // the call to call_stub, the optimizer produces wrong code.
      intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());
      intptr_t* parameter_address = args->parameters();

      StubRoutines::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,
        parameter_address,
        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)

link

仅仅从CallStub上来看link仅仅是一个address,但是是什么address,根据上面代码也可以看出来是JavaCallWrapper,这个类定义在javaCalls.hpp中,基本上算是一个实体类,具体代码如下:

// A JavaCallWrapper is constructed before each JavaCall and destructed after the call.
// Its purpose is to allocate/deallocate a new handle block and to save/restore the last
// Java fp/sp. A pointer to the JavaCallWrapper is stored on the stack.
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(const 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; }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章