admin管理员组文章数量:1576803
从本文开始接受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方法.
后续步骤,下回介绍
本文标签: kvm
版权声明:本文标题:kvm解释器-001 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1727799659a1130674.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论