kvm解释器-001

从本文开始接受kvm中解释器是如何执行的流程.以一个例子来进行说明,如下:

public class KVMTest{
	
   public static void main(String[] args){
   	
      System.out.println("success");	
   } 

	
}

javap后,其结果如下:

# 命令行为: javap -v -l -p -c -s -constants KVMTest.class 
Classfile KVMTest.class
  Last modified Jul 8, 2019; size 415 bytes
  MD5 checksum be7ceda6878f53d188a60205926bab6f
  Compiled from "KVMTest.java"
public class KVMTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // success
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // KVMTest
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               KVMTest.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               success
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               KVMTest
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public KVMTest();
    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 1: 0

  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: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String success
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
}
SourceFile: "KVMTest.java"

注意,此处的java版本为1.8.0_131,但是不影响案例的讲解.因为我们只是看字节码而已.

第一次创建栈帧

在InitializeThreading方法中,首先对SP,FP,IP进行了初始化.如下:

setSP((MainThread->stack->cells - 1));
setFP(NULL);
setIP(KILLTHREAD);

则此时,sp执行栈底的下一个位置,FP为0,ip为1.

然后调用pushFrame(RunCustomCodeMethod)后,此时的结果如图所示(注意,在本案例中,一直假设栈开的足够大,不涉及重新创建stack的情况):

在这里插入图片描述
经过压栈的调用,代码如下:

pushStackAsType(CustomCodeCallbackFunction,
                        initInitialThreadBehaviorFromThread);
pushStackAsType(INSTANCE_CLASS, mainClass);
pushStackAsType(ARRAY, arguments);

则此时情况如图所示:

在这里插入图片描述

创建初始化主类的栈帧

此处是在InitializeThreading方法中,通过调用initializeClass(mainClass)进行创建的.此时的情况如图:

在这里插入图片描述
压栈后的情况如下:

在这里插入图片描述

创建初始化系统类的栈帧

此处是在KVM_Start方法中,通过调用如下代码来进行创建的.

initializeClass(JavaLangOutOfMemoryError);
initializeClass(JavaLangSystem);
initializeClass(JavaLangString);
initializeClass(JavaLangThread);
initializeClass(JavaLangClass);

则此时的情况分别如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Interpret

该方法就是kvm解释执行的环节了。在该方法内部调用了FastInterpret.代码如下:

void Interpret() {

    /* When the VM has handled an exception, it returns here */
startTry:

    CurrentNativeMethod = NULL;

    TRY {

        START_TEMPORARY_ROOTS
		/*
		 *    thisObjectGCSafe = (                                 \
        thisObjectGCSafe = ((void*)0),                                                 \
                                           \
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&thisObjectGCSafe,   \
        thisObjectGCSafe);
		 */
            IS_TEMPORARY_ROOT(thisObjectGCSafe, NULL);
            FastInterpret();
        END_TEMPORARY_ROOTS

    } CATCH (e) {

        /*
         * Exceptions in the FastInterpret() loop must be thrown within 
         * a VMSAVE/VMRESTORE pair.  Otherwise, throwException below
         * will not to get the right values for virtual machine registers
         */
        START_TEMPORARY_ROOTS
		/*
		 * e = (                                 \
        e = e,                                                 \
                                           \
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&e,   \
        e);
		 */
            IS_TEMPORARY_ROOT(e, e);
            throwException(&e);
        END_TEMPORARY_ROOTS

    } END_CATCH_AND_GOTO(startTry)

#if INSTRUMENT
    fprintf(stdout,"bytecodes      =\t%ld\n", bytecodes);
    fprintf(stdout,"slowcodes      =\t%ld\t(%ld)%%\n", slowcodes, slowcodes/(bytecodes/100));
    fprintf(stdout,"calls          =\t%ld\t(%ld)%%\n", calls,         calls/(bytecodes/100));
    fprintf(stdout,"branches taken =\t%ld\t(%ld)%%\n", branches,   branches/(bytecodes/100));
    fprintf(stdout,"rescheduled    =\t%ld\t(%ld)%%\n", reshed,       reshed/(bytecodes/100));
#endif /* INSTRUMENT */
}

而在FastInterpret中,首先定义了ip,sp,lp等寄存器.如下:

#if IPISLOCAL
#undef ip
    register BYTE* ip; /* Instruction pointer (program counter) */
#endif

#if SPISLOCAL
#undef sp
    register cell* sp; /* Execution stack pointer */
#endif

#if LPISLOCAL
#undef lp
    register cell* lp; /* Local variable pointer */
#endif

#if FPISLOCAL
#undef fp
    register FRAME fp; /* Current frame pointer */
#endif

#if CPISLOCAL
#undef cp
    register CONSTANTPOOL  cp; /* Constant pool pointer */
#endif

#if ENABLE_JAVA_DEBUGGER
    register BYTE token;
#endif

又定义了几个interpreter所需要的变量:

METHOD   thisMethod;
OBJECT   thisObject;
int      invokerSize;
const char *exception;

接下来进行准备工作,将ip,sp指向初始化JavaLangClass所建立栈帧的ip,sp.代码如下:

/*
 * {
		ip = GlobalState.gs_ip; ;
		sp = GlobalState.gs_sp; ;
	}
 */
VMRESTORE  /**  保存寄存器中的值到本地变量中 **/

接下来就是一个巨大的switch了.如下:

reschedulePoint:

    goto next0;

next3:  ip++;
next2:  ip++;
next1:  ip++;
next0:


   /*
    * Profile the instruction
    */
    INSTRUCTIONPROFILE

   /*
    * Trace the instruction here if the option is enabled
    */
    INSTRUCTIONTRACE

   /*
    * Increment the bytecode counter 空操作
    */
    INC_BYTECODES

   /*
    *  Extreme debug option to call the garbage collector before every bytecode 是否每次执行字节码都执行gc
    */
    DO_VERY_EXCESSIVE_GARBAGE_COLLECTION

   /*
    * Dispatch the bytecode
    */

    switch (((unsigned char)*ip)) {
    
    // 省略各种对字节码的处理
    
    
    default: {
            sprintf(str_buffer, KVM_MSG_ILLEGAL_BYTECODE_1LONGPARAM,
                    (long)TOKEN);
            fatalError(str_buffer);
            break;
        }
    }
    fatalError(KVM_MSG_INTERPRETER_STOPPED);


解释执行对class的初始化

由于此时ip指向的runCustomCode所指向的字节码,而在InitializeJavaSystemClasses中所对该方法的处理:

// 2.2 获得runCustomCode方法
RunCustomCodeMethod = getSpecialMethod(JavaLangClass,
                           getNameAndTypeKey("runCustomCode", "()V"));

/* Patch the bytecode, was a "return" 2.3 将runCustomCode方法的字节码修改为CUSTOMCODE */
if (RELOCATABLE_ROM) {
    /* Do nothing. Already patched */
} else if (!USESTATIC || (inAnyHeap(RunCustomCodeMethod->u.java.code))){
    RunCustomCodeMethod->u.java.code[0] = CUSTOMCODE;
    RunCustomCodeMethod->u.java.maxStack =
        RunCustomCodeMethod_MAX_STACK_SIZE;
} else {
    BYTE newcode = CUSTOMCODE;
    short newMaxStack = RunCustomCodeMethod_MAX_STACK_SIZE;
    char *start = (char*)((INSTANCE_CLASS)JavaLangClass)->constPool;
    int offset = (char*)(RunCustomCodeMethod->u.java.code) - start;
    modifyStaticMemory(start, offset, &newcode, sizeof(newcode));

    offset = (char *)(&RunCustomCodeMethod->u.java.maxStack) - start;
    modifyStaticMemory(start, offset,
                       &newMaxStack, sizeof(newMaxStack));
}

不管如何,可以知道的是 JavaLangClass中的runCustomCode 的字节码是CUSTOMCODE,其maxStack为4,其参数为0,返回值为0.其在JavaLangClass中的定义如下:

/*
 * This private function is used during virtual machine initialization.
 * The user does not normally see this function.
 */
private static void runCustomCode() {}

因此在那个巨大的switch中,最终会执行如下代码:

#if INFREQUENTSTANDARDBYTECODES
SELECT(CUSTOMCODE)      // 宏展开为 case CUSTOMCODE: {                        /* 0xDF */
        cell *stack = (cell*)(fp + 1);// 取得栈底
        CustomCodeCallbackFunction func = *(CustomCodeCallbackFunction *)stack;// 取得对应的函数
        
        // 如果函数存在,则执行之
        if (func != NULL) {
            VMSAVE
            func(NULL);
            VMRESTORE
        }
        else {
            POP_FRAME
        }
DONE_R // 宏展开为 } goto reschedulePoint;
#endif

此处结合下图,可以得知此处最终会执行runClinit方法.

在这里插入图片描述

后续步骤,下回介绍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值