kvm解释器-002
在JAVA虚拟机规范(JAVA SE 7 )中,针对初始化的过程有如下的定义:每个类或接口C,都有一个唯一的初始化锁LC。如何实现从C到LC的映射可由Java虚拟机实现自行决定。例如,LC可以是C的Class对象,或者是与Class对象相关的管程(Monitor)。初始化C的过程如下:同步C的初始化锁LC。这个操作会导致当前线程一直等待直到可以获得LC锁如果C的Class对象显示当前C的初...
在JAVA虚拟机规范(JAVA SE 7 )中,针对初始化的过程有如下的定义:
每个类或接口C,都有一个唯一的初始化锁LC。如何实现从C到LC的映射可由Java虚拟机实现自行决定。例如,LC可以是C的Class对象,或者是与Class对象相关的管程(Monitor)。初始化C的过程如下:
- 同步C的初始化锁LC。这个操作会导致当前线程一直等待直到可以获得LC锁
- 如果C的Class对象显示当前C的初始化是由其它线程正在进行,那么当前线程释放LC并进入阻塞状态,直到它知道初始化工作已经由其它线程完成,那么当前线程在此重试此步骤。
- 如果C的 Class对象显示C的初始化正由当前线程在做,这就是对初始化的递归请求。释放LC并正常返回。
- 如果C的Class对象显示Class已经被初始化完成,那么什么也不做。释放LC并正常返回。
- 如果C的Class对象显示它处于一个错误的状态,就不再初始化了。释放LC并抛出NoClassDefFoundError异常。
- 否则,记录下当前线程正在初始化C的Class对象,随后释放LC。根据属性出现在ClassFile的顺序,利用常量池中的ConstantValue属性(§4.7.2)来初始化C中的各个final static字段。
- 接下来,如果C是类而不是接口,而且它的父类SC还没有被初始化过,那就对于SC也进行完整的初始化过程。当然如果必要的话,需要先验证和准备SC。如果在初始化SC的时候因为抛出异常而中断,那么就获取LC后将C的Class对象标识为错误状态,并通知所有正在等待的线程,最后释放LC并异常退出,抛出与SC初始化遇到的异常相同的异常。
- 之后,通过查询C的定义加载器来决定是否为C开启断言机制。
- 执行C的类或接口初始化方法。
- 如果正常地执行了类或接口的初始化方法,之后就请求获取LC,标记C的Class对象已经被完全初始化,通知所有正在等待的线程,接着释放LC,正常地退出整个过程。
- 否则,类或接口的初始化方法就必须抛出一个异常E并中断退出。如果E不是Error或它的某个子类,那就创建一个新的ExceptionInInitializerError实例,然后将此实例作为E的参数,之后的步骤就使用E这个对象。如果因为OutOfMemoryError问题而不能创建ExceptionInInitializerError实例,那在之后就使用OutOfMemoryError异常对象作为E的参数。
- 获取LC,标记下C的Class对象有错误发生,通知所有的等待线程,释放LC,将E或上述的具体错误对象作为此次意外中断的原因。
runClinit
而在KVM中的runClinit也是按照以上的规范来进行的.其代码一开始会获得初始化的状态和要实例化的class.代码如下:
INSTANCE_CLASS thisClass;
int state;
bool_t haveMonitor = FALSE;
if (exceptionFrameH) {
runClinitException(exceptionFrameH);
return;
}
state = topStackAsType(cell);
thisClass = secondStackAsType(INSTANCE_CLASS);
针对我们的案例,此处获得的值分别为1,JavaLangClass.如图所示:
由于此处状态为1,因此会执行如下代码:
if (!OBJECT_HAS_MONITOR(&thisClass->clazz)
&& thisClass->initThread == NULL) {
goto markClass;// 此处执行这里
}
/* Step 1: Grab the class monitor so we have exclusive access. 同步C的初始化锁LC。这个操作会导致当前线程一直等待直到可以获得LC锁。 */
if (monitorEnter((OBJECT)thisClass) != MonitorStatusOwn) { // 如果有其他线程在初始化,则等待
/* We've been forced to wait. When we're awoken, we'll have
* the lock */
topStack = 2;
return;
} else {
/* FALL THROUGH. We have the lock */
}
由于此时JavaLangClass的initThread为null,因此会执行markClass处的代码.如下:
markClass:
if (thisClass->initThread == CurrentThread ||
thisClass->status == CLASS_READY) { /* step 4 */
/* Step 3, Step 4:
* This thread is already initializing the class, or the class
* has somehow already become initialized. We're done.
* 如果C的 Class对象显示C的初始化正由当前线程在做,这就是对初始化的递归请求。释放LC并正常返回。
* 如果C的Class对象显示Class已经被初始化完成,那么什么也不做。释放LC并正常返回。
*/
if (haveMonitor) {
char *junk;
monitorExit((OBJECT)thisClass, &junk);
}
popFrame();
return;
}
if (thisClass->status == CLASS_ERROR) {
/* Step 5:
* What can we do?
* 如果C的Class对象显示它处于一个错误的状态,就不再初始化了。释放LC并抛出NoClassDefFoundError异常。
*/
if (haveMonitor) {
char *junk;
monitorExit((OBJECT)thisClass, &junk);
}
popFrame();
return;
}
/* Step 6:
* Mark that we're about to initialize this class 记录下当前线程正在初始化C的Class对象,随后释放LC*/
setClassInitialThread(thisClass, CurrentThread);// 此处执行这里
if (haveMonitor) {
char *junk;
monitorExit((OBJECT)thisClass, &junk);
haveMonitor = FALSE;
}
/* FALL THROUGH */
此处重点是执行setClassInitialThread,代码如下:
#define setClassInitialThread(iclazz, thread) \
((iclazz)->initThread = (thread))
注意,此处的CurrentThread为MainThread,这点在kvm启动流程-006中有介绍.
接下来执行如下代码:
if ((thisClass->clazz.accessFlags & ACC_INTERFACE) == 0) {
INSTANCE_CLASS superClass = thisClass->superClass;
if (superClass && superClass->status != CLASS_READY) {
topStack = 4;
initializeClass(superClass);
return;
}
}
由于class的父类为object,且一直没有初始化完毕,即其状态不是CLASS_READY,因此此处会首先将class的状态改为4,然后初始化object.此时的情况如下:
而在initializeClass中,由于object不存在父类,因此会执行如下代码:
if ((thisClass->superClass == NULL ||
thisClass->superClass->status == CLASS_READY) &&
getSpecialMethod(thisClass,clinitNameAndType) == NULL) {
setClassStatus(thisClass,CLASS_READY);
}
之后,一路return,返回到Interpret的那个大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执行前后,分别使用了VMSAVE,VMRESTORE将ip,sp,fp等寄存器的值保存和恢复.
因此,此处在goto reschedulePoint 时,其ip仍是指向RunCustomCodeMethod所对应的字节码,即runClinit.sp仍是指向初始化class所对应的栈,fp认识指向class所对应的frame…
因此,在那个Interpret的那个大switch中,仍会跳到对CUSTOMCODE的处理,此时的栈的情况如图:
因此在runClinit方法中,由于此时的状态为4,因此会执行如下代码:
case 4: {
/* Step 8:
* Run the <clinit> method, if the class has one
* 执行C的类或接口初始化方法。
*
*/
METHOD thisMethod = getSpecialMethod(thisClass, clinitNameAndType);
if (thisMethod) {// 对于class来说,是不存在<clinit>方法的
#if INCLUDEDEBUGCODE
if (traceclassloading || traceclassloadingverbose) {
START_TEMPORARY_ROOTS
fprintf(stdout, "Initializing class: '%s'\n",
getClassName((CLASS)thisClass));
END_TEMPORARY_ROOTS
}
#endif /* INCLUDEDEBUGCODE */
topStack = 5;
pushFrame(thisMethod);
return;
} else {
/* No <clinit> method. */
/* FALL THROUGH */
}
}
case 5:
/* Step 9:
* Grab the monitor so we can change the flags, and wake up any
* other thread waiting on us.
* 如果正常地执行了类或接口的初始化方法,之后就请求获取LC,标记C的Class对象已经被完全初始化,通知所有正在等待的线程,接着释放LC,正常地退出整个过程。
* SHORTCUT: 99% of the time, there is no contention for the class.
* Since our scheduler is non-preemptive, if there is no contention
* for this class, we just go ahead and unmark the class, without
* bothering with the monitor.
*/
if (!OBJECT_HAS_MONITOR(&thisClass->clazz)) {
goto unmarkClass; // 此处最终执行这里
}
if (monitorEnter((OBJECT)thisClass) != MonitorStatusOwn) {
/* When we wake up, we'll have the monitor */
topStack = 6;
return;
} else {
/* FALL THROUGH */
}
在当前情况,最终会执行unmarkClass处的代码.
unmarkClass:
setClassInitialThread(thisClass, NULL);
setClassStatus(thisClass, CLASS_READY);
#if ENABLE_JAVA_DEBUGGER
if (vmDebugReady) {
CEModPtr cep = GetCEModifier();
cep->loc.classID = GET_CLASS_DEBUGGERID(&thisClass->clazz);
cep->threadID = getObjectID((OBJECT)CurrentThread->javaThread);
cep->eventKind = JDWP_EventKind_CLASS_PREPARE;
insertDebugEvent(cep);
}
#endif /* ENABLE_JAVA_DEBUGGER */
if (haveMonitor) {
char *junk;
monitorNotify((OBJECT)thisClass, TRUE); /* wakeup everyone */
monitorExit((OBJECT)thisClass, &junk);
}
popFrame();
return;
此处最终将class的状态设置为CLASS_READY,然后执行弹栈操作. popFrame的代码如下:
void popFrame()
{
/* Unallocate a stack frame and restore virtual */
/* machine registers to continue the execution */
/* of the previous method. */
#if INCLUDEDEBUGCODE
if (tracemethodcalls || tracemethodcallsverbose) {
frameTracing(getFP()->thisMethod, "<=", 0);
}
#endif /* INCLUDEDEBUGCODE */
/* See frame.h */
POPFRAMEMACRO
#if INCLUDEDEBUGCODE
if (traceframes) {
fprintf(stdout,
"Popped a stack frame (thread: %lx, fp: %lx, sp: %lx, depth: %ld, stack: %lx)\n",
(long)CurrentThread, (long)getFP(),
(long)getSP(), (long)frameDepth(), (long)getFP()->stack);
}
#endif /* INCLUDEDEBUGCODE */
#if ENABLE_JAVA_DEBUGGER
if (vmDebugReady)
setEvent_FramePop();
#endif
}
这里最重要的是POPFRAMEMACRO,其宏展开如下:
#define POPFRAMEMACRO \
setSP(getFP()->previousSp); /* Restore previous stack pointer */ \
setIP(getFP()->previousIp); /* Restore previous instruction pointer */ \
setFP(getFP()->previousFp); /* Restore previous frame pointer */ \
setLP(FRAMELOCALS(getFP())); /* Restore previous locals pointer */ \
setCP(getFP()->thisMethod->ofClass->constPool);
因此此时的情况如下:
其最终会执行对thread类的初始化.
更多推荐
所有评论(0)