Hotspot源码解析一


试了一天,windows一直失败.无奈了.安装了一个linux明天试试.
詹姆斯老爷子通过一种中间语言形式,兼容所有操作系统.刚开始是通过C进行编译,但是效率极低,为了提高中间语言执行效率,老爷子从指令集出发,高度抽象出了java指令集.从而当一个程序编译成中间语言也就是class后,运行时会将字节码指令动态转成本地机器指令,从而运行.牛逼詹老爷子.

hotspot源码的确比较恶心,要懂C/C++,还要懂ASM,算法也要掌握.直接劝退.看了一整天,本来迷迷糊糊,现在更加迷迷糊糊了.= =

call_stub

javaCalls.cpp

StubRoutines::call_stub()(
              //连接器
        (address)&link,
        // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
        //函数返回值地址
        result_val_address,          // see NOTE above (compiler problem)
        //返回类型
        result_type,
        //jvm内部所表示的java方法对象,可获取java函数编译后的字节码信息,如继承,返回,注解,入参,函数名称,返回值等信息
        method(),
        //jvm调用java方法的例程入口,也就是预生成的机器指令.从这里拿到java函数所对应的第一个字节码指令,然后调用java函数
        entry_point,
        //入参集合
        args->parameters(),
        //入参数量
        args->size_of_parameters(),
        //当前线程对象
        CHECK
      );

stubRoutines.hpp

  static CallStub call_stub(){
      return CAST_TO_FN_PTR(CallStub, _call_stub_entry);
      //等同于 ((CallStub)(castable_address(_call_stub_entry)))  =》  address_word(_call_stub_entry) ;
      //在linux上 (CallStub)unsigned int(_call_stub_entry)

      //unsigned int(unsigned char * )
  }

CAST_TO_FN_PTR在globalDefinitions.hpp定义,

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

castable_address声明为一个内联函数

inline address_word  castable_address(address x)              { return address_word(x) ; }

address_word的类型

/*
 * 分别三个平台定义了
 * linux   globalDefinitions_gcc.hpp
 */
typedef uintptr_t     address_word;

我们查看linux平台
stubGenerator_x86_32.cpp

typedef unsigned int            uintptr_t;

可以看得到
CAST_TO_FN_PTR(CallStub, _call_stub_entry)<=>(CallStub)unsigned int(_call_stub_entry)

那么_call_stub_entry的值是啥了?
stubRoutines.hpp

  static address _call_stub_entry;

globalDefinitions.hpp

typedef unsigned char u_char;
typedef u_char*       address;

可以看得出最终的形式为:
(CallStub)uunsigned int(unsigned char * )
在这里插入图片描述
_call_stub_entry的赋值过程:
stubGenerator_x86_32.cpp

    //给_call_stub_entry赋值
    StubRoutines::_call_stub_entry              =
      generate_call_stub(StubRoutines::_call_stub_return_address);

自此初始化完成,紧接着就是调用了.
javaCalls.cpp

HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner

      StubRoutines::call_stub()(
              //连接器
        (address)&link,
        // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
        //函数返回值地址
        result_val_address,          // see NOTE above (compiler problem)
        //返回类型
        result_type,
        //jvm内部所表示的java方法对象,可获取java函数编译后的字节码信息,如继承,返回,注解,入参,函数名称,返回值等信息
        method(),
        //jvm调用java方法的例程入口,也就是预生成的机器指令.从这里拿到java函数所对应的第一个字节码指令,然后调用java函数
        entry_point,
        //入参集合
        args->parameters(),
        //入参数量
        args->size_of_parameters(),
        //当前线程对象
        CHECK
      );

link

JavaCallWrapper link(method, receiver, result, CHECK);

连接器,主要将java函数调用者和被调用者搭建桥梁
javaCalls.hpp

//连接器,主要将java函数调用者和被调用者搭建桥梁
class JavaCallWrapper: StackObj {
  friend class VMStructs;
 private:
    //当前java函数所在线程
  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
  //被调用者,非静态java方法
  oop              _receiver;               // the receiver of the call (if a non-static call)
  //java线程堆栈对象
  JavaFrameAnchor  _anchor;                 // last thread anchor state that we must restore
  //java方法所返回的值
  JavaValue*       _result;                 // result value

entry_point执行过程
在这里插入图片描述

_call_stub_entry例程

JVM初始化链路
在这里插入图片描述
在红框处对**_call_stub_entry**进行了初始化

stubGenerator_x86_32.cpp

    //给_call_stub_entry赋值
    StubRoutines::_call_stub_entry              =
      generate_call_stub(StubRoutines::_call_stub_return_address);

generate_call_stub

address generate_call_stub(address& return_address) {
    StubCodeMark mark(this, "StubRoutines", "call_stub");
    //得到当前入口的内存地址
    address start = __ pc();

    // stub code parameters / addresses
    assert(frame::entry_frame_call_wrapper_offset == 2, "adjust this code");
    bool  sse_save = false;
    const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_catch_exception()!
    const int     locals_count_in_bytes  (4*wordSize);

    //相当于 &result = 3N(%ebp),从内存中获取数据
    //如下参数在被调用者内部.用于保存调用者的信息
    const Address mxcsr_save    (rbp, -4 * wordSize);
    const Address saved_rbx     (rbp, -3 * wordSize);
    const Address saved_rsi     (rbp, -2 * wordSize);
    const Address saved_rdi     (rbp, -1 * wordSize);

    //如下参数在调用者内部
    const Address result        (rbp,  3 * wordSize);
    const Address result_type   (rbp,  4 * wordSize);
    const Address method        (rbp,  5 * wordSize);
    const Address entry_point   (rbp,  6 * wordSize);
    const Address parameters    (rbp,  7 * wordSize);
    const Address parameter_size(rbp,  8 * wordSize);
    const Address thread        (rbp,  9 * wordSize); // same as in generate_catch_exception()!
    sse_save =  UseSSE > 0;

    // stub code
    //保存调用者函数栈基地址,设置被调用者栈基地址
    __ enter();

    //计算即将被调用java函数入参所需堆栈空间
    //movl 0x20(%ebp) %ecx  将parameter_size值传给ecx寄存器
    __ movptr(rcx, parameter_size);              // parameter counter
    //shl $0x2 %ecx          将ecx寄存器值左移2位,也就是*4,因为32平台,每个指针占用32内存,也就是4字节.
    __ shlptr(rcx, Interpreter::logStackElementSize); // convert parameter count to bytes

    //add $0x10,%ecx   额外申请16字节空间,用于存储rdi,rsi,rbx,mxcsr4个寄存器值
    __ addptr(rcx, locals_count_in_bytes);       // reserve space for register saves
    //sub %ecx,%esp 申请rcx大小堆栈空间
    __ subptr(rsp, rcx);
    //and $0xfffffff0,%esp 堆栈按16位对齐,若不对齐则减去后4位值
    __ andptr(rsp, -(StackAlignmentInBytes));    // Align stack

    // save rdi, rsi, & rbx, according to C calling conventions
    //保存rdi,rsi,rbx 调用者现场
    //mov %edi,-0x4(%ebp)
    __ movptr(saved_rdi, rdi);
    // mov %esi,-0x8(%ebp)  esi用于java字节码寻址
    __ movptr(saved_rsi, rsi);
    // mov %ebx,-0xc(%ebp)
    __ movptr(saved_rbx, rbx);

    // save and initialize %mxcsr
    if (sse_save) {
      Label skip_ldmx;
      __ stmxcsr(mxcsr_save);
      __ movl(rax, mxcsr_save);
      __ andl(rax, MXCSR_MASK);    // Only check control and mask bits
      ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std());
      __ cmp32(rax, mxcsr_std);
      __ jcc(Assembler::equal, skip_ldmx);
      __ ldmxcsr(mxcsr_std);
      __ bind(skip_ldmx);
    }

    // make sure the control word is correct.
    __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std()));

#ifdef ASSERT
    // make sure we have no pending exceptions
    { Label L;
      __ movptr(rcx, thread);
      __ cmpptr(Address(rcx, Thread::pending_exception_offset()), (int32_t)NULL_WORD);
      __ jcc(Assembler::equal, L);
      __ stop("StubRoutines::call_stub: entered with pending exception");
      __ bind(L);
    }
#endif

    // pass parameters if any
    BLOCK_COMMENT("pass parameters if any");
    Label parameters_done;

    //参数数量
    //mov 0x20(%ebp),%ecx  将parameter_size存ecx
    __ movl(rcx, parameter_size);  // parameter counter
    //测试parameter_size是否=0,若是则直接跳过参数处理
    //test %ecx,ecx
    __ testl(rcx, rcx);
    __ jcc(Assembler::zero, parameters_done);

    // parameter passing loop

    Label loop;
    // Copy Java parameters in reverse order (receiver last)
    // Note that the argument order is inverted in the process
    // source is rdx[rcx: N-1..0]
    // dest   is rsp[rbx: 0..N-1]

    //第一个入参地址
    //mov 0x1c(%ebp),%edx  用edx记录第一个入参指针
    __ movptr(rdx, parameters);          // parameter pointer

    //%ebx设0
    //xor %ebx,%ebx
    __ xorptr(rbx, rbx);

    //循环将java函数参数压栈
    __ BIND(loop);

    // get parameter
    // 获取第N个入参位置:edx+(N-1)*4
    //mov -0x4(%edx,%ecx,4),%eax
    __ movptr(rax, Address(rdx, rcx, Interpreter::stackElementScale(), -wordSize));
    //mov %eax,(%esp,%ebx,4)
    __ movptr(Address(rsp, rbx, Interpreter::stackElementScale(),
                    Interpreter::expr_offset_in_bytes(0)), rax);          // store parameter

    //inc %ebx
    __ increment(rbx);
    //dec %ecx 参数数量-1,也就是循环次数-1,这里主要采用逆序遍历
    __ decrement(rcx);
    //jne 0x地址
    __ jcc(Assembler::notZero, loop);

    // call Java function
    __ BIND(parameters_done);
    //mov 0x14(%ebp),%ebx  将method首地址传给ebx
    __ movptr(rbx, method);           // get Method*
    //mov 0x18(%ebp),%eax  将entry_point传给eax
    __ movptr(rax, entry_point);      // get entry_point
    //mov %esp,%esi 将当前栈顶保存esi中
    __ mov(rsi, rsp);                 // set sender sp
    BLOCK_COMMENT("call Java function");
    //call *%eax    调用entry_point
    __ call(rax);

    BLOCK_COMMENT("call_stub_return_address:");
    return_address = __ pc();

JAVA数据结构与面向对象

oop-klass模型

  • oop,用来描述对象实例信息
  • klass,描述class

oop体系
oopsHierarchy.hpp
定义的各种oop(用来描述对象实例信息)

typedef class oopDesc*                            oop;
typedef class   instanceOopDesc*            instanceOop;
typedef class   arrayOopDesc*                    arrayOop;
typedef class     objArrayOopDesc*            objArrayOop;
typedef class     typeArrayOopDesc*            typeArrayOop;

定义的各种class信息

class Klass;
class   InstanceKlass;
class     InstanceMirrorKlass;
class     InstanceClassLoaderKlass;
class     InstanceRefKlass;
class   ArrayKlass;
class     ObjArrayKlass;
class     TypeArrayKlass;

oop.hpp

//所有oopDesc的父类
class oopDesc {
  friend class VMStructs;
 private:
    //标记,线程状态,并发锁,GC分代信息等内部标识
  volatile markOop  _mark;
    //标识元数据信息,如变量,方法,父类,所实现接口等信息
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;

在这里插入图片描述

解析魔数

classFileParser.cpp

  ClassFileStream* cfs = stream();
  ...
  //解析魔数
  u4 magic = cfs->get_u4_fast();

ClassFileStream主要用于读取字节码流中数据,结构如下:
classFileStream.hpp

class ClassFileStream: public ResourceObj {
 private:
  u1*   _buffer_start; // Buffer bottom
  u1*   _buffer_end;   // Buffer top (one past last element)
  //当前字节码流读到位置
  u1*   _current;      // Current buffer position
  char* _source;       // Source of stream (directory name, ZIP/JAR archive name)
  bool  _need_verify;  // True if verification is on for the class file

从字节码流中读取4字节内容

  u4 get_u4_fast() {
    u4 res = Bytes::get_Java_u4(_current);
    _current += 4;
    return res;
  }

bytes_x86.hpp

  /**
   * 从内存指定位置读取4字节并转成JVM内部u4类型
   */
  static inline u4   get_Java_u4(address p)           { return swap_u4(get_native_u4(p)); }
  /**
   * 从内存指定位置读取4字节并转成u4类型
   */
  static inline u4   get_native_u4(address p)         { return *(u4*)p; }

大小端转换
bytes_linux_x86.inline.hpp

/**
 * u4类型大端小端转换
 */
inline u4   Bytes::swap_u4(u4 x) {
#ifdef AMD64
  return bswap_32(x);
#else
  u4 ret;
  __asm__ __volatile__ (
    "bswap %0"
    :"=r" (ret)      // output : register 0 => ret
    :"0"  (x)        // input  : x => register 0
    :"0"             // clobbered register
  );
  return ret;
#endif // AMD64
}

java字节码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值