本文继续介绍kvm中解释执行的步骤.接上文,目前栈中的情况如图所示:
初始化Thread
此处初始化的步骤和上文中介绍的一样,也是调用runClinit方法.首先设置初始化线程为CurrentThread,然后由于其父类Object此时的状态为CLASS_READY,因此就不需要初始化父类了.此处的代码如下:
if ((thisClass->clazz.accessFlags & ACC_INTERFACE) == 0) {
INSTANCE_CLASS superClass = thisClass->superClass;
if (superClass && superClass->status != CLASS_READY) {
topStack = 4;
initializeClass(superClass);
return;
}
}
同样由于thread没有方法,因此会直接修改状态为CLASS_READY,然后进行弹栈操作.则此时栈的情况如图所示:
初始化String,System,OutOfMemoryError
对于String, System而言,其初始化过程和 Thread一致.而对于OutOfMemoryError而言,其继承结构如下:
因此对于OutOfMemoryError,会首先将状态改为4,然后尝试初始化VirtualMachineError,同样的VirtualMachineError将状态改为4,然后初始化Error,然后初始化Throwable.对于Throwable而言,其父类为Object已经为CLASS_READY,因此在runClinit方法中,直接将状态改为CLASS_READY,然后弹栈.接下来是Error,将状态改为CLASS_READY,然后弹栈.然后是VirtualMachineError,将状态改为CLASS_READY,然后弹栈.最终将OutOfMemoryError状态改为CLASS_READY,然后弹栈.则此时的栈的情况如图所示:
其实对于System而言,由于其内部有个静态字段.如下:
public final static PrintStream out = getOutput();
private static PrintStream getOutput() {
return new PrintStream(new com.sun.cldc.io.ConsoleOutputStream());
}
因此,javac会生成一个方法.进行out字段的初始化.不过这里我就不展开了.
初始化主类
由于我们的主类代码如下:
public class KVMTest{
public static void main(String[] args){
System.out.println("success");
}
}
因此在runClinit方法中,直接将状态改为CLASS_READY,然后弹栈(父类Object的状态已经是CLASS_READY).则此时的栈的情况如下:
调用主类方法
此处同样,对应的字节码是CUSTOMCODE,其最终调用的是initInitialThreadBehaviorFromThread.此处的代码如下:
static void
initInitialThreadBehaviorFromThread(FRAME_HANDLE exceptionFrameH) {
INSTANCE_CLASS thisClass;
METHOD thisMethod;
// 1. 如果有异常,则直接return
if (exceptionFrameH != NULL) {
/* We have no interest in dealing with exceptions. */
return;
}
thisClass = secondStackAsType(INSTANCE_CLASS);// 获得主类名称
// 2. 获得main方法
thisMethod = getSpecialMethod(thisClass, mainNameAndType);
if (thisMethod == NULL) {// 如果main方法不存在,则终止执行
AlertUser(KVM_MSG_CLASS_DOES_NOT_HAVE_MAIN_FUNCTION);
stopThread();
} else if ((thisMethod->accessFlags & ACC_PUBLIC) == 0) {// 如果main方法的修饰符不是public,则终止执行
AlertUser(KVM_MSG_MAIN_FUNCTION_MUST_BE_PUBLIC);
stopThread();
} else {
START_TEMPORARY_ROOTS
/*
* ARRAY arguments = (
arguments = (*(ARRAY *)((GlobalState.gs_sp))),
TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&arguments,
arguments)
*/
DECLARE_TEMPORARY_ROOT(ARRAY, arguments, topStackAsType(ARRAY));
/* Reinitialize the thread for the new method 3. 由于此处是最后一个栈帧,则需要做一些初始化工作*/
setSP((CurrentThread->stack->cells - 1) + thisMethod->argCount); // 复用参数
setFP(NULL);
setIP(KILLTHREAD);
pushFrame(thisMethod);
((ARRAY *)getLP())[0] = arguments;
END_TEMPORARY_ROOTS
// 4. 对同步的处理
if (thisMethod->accessFlags & ACC_SYNCHRONIZED) {
getFP()->syncObject = (OBJECT)thisClass;
monitorEnter((OBJECT)thisClass);
} else {
getFP()->syncObject = NULL;
}
}
}
这里比较重要的是第3步.由于当前是最后一个栈帧,因此将sp设置为(CurrentThread->stack->cells - 1) + thisMethod->argCount.(为何这样设置,是因为就相当于隐式弹栈一样)。此时的情况如图所示
在pushFrame后,栈的情况为:
在经过 ((ARRAY *)getLP())[0] = arguments的处理,则最终栈的情况为:
关于main方法的执行,下文介绍.