OpenJDK9 Hotspot :Zero 解释器 - Stack & Frame

前言

虚拟机在解释执行字节码的时候一个重要的抽象就是要模拟 堆栈(Stack) 以及函数(方法)调用的栈帧(Frame),本文简要介绍 Zero 解释器中关于 Stack 和 Frame 的具体实现,相关源代码:

  • hotspot/src/cpu/zero/vm

  • hotspot/src/os_cpu/linux_zero

ZeroStack 类

相关源代码:

  • hotspot/src/cpu/zero/vm/stack_zero.hpp

  • hotspot/src/cpu/zero/vm/stack_zero.cpp

类属性

  • _base,the last available word,基地址

  • _top,the word past the end of the stack

  • _sp,the top word on the stack,当前栈顶指针

class ZeroStack {
private:
    intptr_t *_base;
    intptr_t *_top;
    intptr_t *_sp;
}

附上一张图形象描述 ZeroStack 所抽象的 Stack
OpenJDK9 Hotspot :Zero 解释器 - Stack & Frame_第1张图片

入栈

堆栈是从高地址向低地址伸展,先递减 _sp,再将 value 放入 _sp 指向的位置

void push(intptr_t value) {
    assert(_sp > _base, "stack overflow");
    *(-- _sp) = value;
}

出栈

同理,先取出 _sp 指向的位置的值,再递增 _sp

intptr_t pop() {
    assert(_sp < _top, "stack underflow");
    return *(_sp++);
}

ZeroFrame

相关源代码:

  • hotspot/src/cpu/zero/vm/stack_zero.hpp

ZeroFrame 是对栈帧的抽象,它是一个基类,定义了各个子类公用的 frame header(头部):

  • frame_type,frame 类型

  • next_frame,?

//
// |  ...               |
// +--------------------+  ------------------
// |  ...               |       low addresses
// | frame_type         |
// | next_frame         |      high addresses
// +--------------------+  ------------------
// |  ...               |

Layout

Layout 枚举类型定义了 zero frame header 中的 next_frame 和 frame_type 偏移量

enum Layout {
    next_frame_off,
    frame_type_off,
    jf_header_words
}

访问字段

ZeroFrame 类和 hotspot 源代码中很多其它类一样,采用了"紧凑"的内存布局,没有将类字段声明在类定义里,而是通过 this 指针去操作字段,比如下面的这一组函数

  intptr_t *addr_of_word(int offset) const {
    return (intptr_t *) this - offset;
  }
  intptr_t value_of_word(int offset) const {
    return *addr_of_word(offset);
  }

类型转换

ZeroFrame 提供了一组函数用于将 this 解析成指定的子类指针,因为不同的子类公用相同的头部,所以这种转换是 ok 的~

  EntryFrame *as_entry_frame() const {
    assert(is_entry_frame(), "should be");
    return (EntryFrame *) this;
  }
  InterpreterFrame *as_interpreter_frame() const {
    assert(is_interpreter_frame(), "should be");
    return (InterpreterFrame *) this;
  }
  SharkFrame *as_shark_frame() const {
    assert(is_shark_frame(), "should be");
    return (SharkFrame *) this;
  }
  FakeStubFrame *as_fake_stub_frame() const {
    assert(is_fake_stub_frame(), "should be");
    return (FakeStubFrame *) this;
  }

Frame type

FrameType 枚举类型定义了各个不同的 frame type

enum FrameType {
    ENTRY_FRAME = 1,
    INTERPRETER_FRAME,
    SHARK_FRAME,
    FAKE_STUB_FRAME
}

ZeroFrame 类提供了一组方法用于判断 frame type

  bool is_entry_frame() const {
    return type() == ENTRY_FRAME;
  }
  bool is_interpreter_frame() const {
    return type() == INTERPRETER_FRAME;
  }
  bool is_shark_frame() const {
    return type() == SHARK_FRAME;
  }
  bool is_fake_stub_frame() const {
    return type() == FAKE_STUB_FRAME;
  }

InterpreterFrame

InterpreterFrame 类是 ZeroFrame 类的子类,和 java 方法调用相关,硬要从字面上翻译的化,姑且叫做 "解释器栈帧",它在 ZeroFrame 内存布局(header)的基础上 加了点自己的东西:

  • interpreter state,解释器上下文

  • monitor,synchronize 相关

  • local variable & parameters,局部变量和方法参数(是不是和 C/C++ 堆栈很像~)

// |  ...               |
// +--------------------+  ------------------
// | stack slot n-1     |       low addresses
// |  ...               |
// | stack slot 0       |
// | monitor 0 (maybe)  |
// |  ...               |
// | interpreter state  |
// |  ...               |
// | frame_type         |
// | next_frame         |      high addresses
// +--------------------+  ------------------
// |  ...               |

build 函数中传入了一个 Method 类型的对象(指针),Method 类是 Java 中的方法在虚拟机的内部表示

InterpreterFrame 创建

参考之前相关文章,zero 解释器的入口函数 normal_entry 中会创建一个初始的 InterpreterFrame 并开始执行 byte code

// cppInterpreter_zero.cpp

int CppInterpreter::normal_entry(Method* method, intptr_t UNUSED, TRAPS) {
  JavaThread *thread = (JavaThread *) THREAD;

  // Allocate and initialize our frame.
  InterpreterFrame *frame = InterpreterFrame::build(method, CHECK_0);
  thread->push_zero_frame(frame);

  // Execute those bytecodes!
  main_loop(0, THREAD);

  // No deoptimized frames on the stack
  return 0;
}

InterpreterFrame::build 函数比较长,仔细看来,无非就做了这几件事:

  • 为本地变量,函数参数预留空间

  • 在栈帧中创建解释器上下文(interpreter state)对象并进行初始化

总结

你可能感兴趣的:(jvm,虚拟机,堆栈上下文)