kvm启动流程-006

本文介绍kvm启动过程中比较复杂的步骤,初始化多线程系统与初始化系统类

初始化多线程系统

此处的代码如下:

void InitializeThreading(INSTANCE_CLASS mainClass, ARRAY argumentsArg)
{
    START_TEMPORARY_ROOTS
	    /*
	     * ARRAY arguments = (
        arguments = argumentsArg,
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&arguments,
        arguments)
	     */
        DECLARE_TEMPORARY_ROOT(ARRAY, arguments, argumentsArg);
        /*
         *  JAVATHREAD javaThread = (                                 \
        javaThread = (JAVATHREAD)instantiate(JavaLangThread),                                                 \
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&javaThread,   \
        javaThread);
         */
        DECLARE_TEMPORARY_ROOT(JAVATHREAD, javaThread,
                               (JAVATHREAD)instantiate(JavaLangThread));
        
        int unused; /* Needed for creating name char array */
        makeGlobalRoot((cell **)&MainThread); // 将主线程加入到root中
        MainThread = NULL;
        MonitorCache = NULL;
        makeGlobalRoot((cell **)&CurrentThread);
        makeGlobalRoot((cell **)&RunnableThreads);
        makeGlobalRoot((cell **)&TimerQueue);

        /* Initialize the field of the Java-level thread structure 设置优先级为5*/
        javaThread->priority = 5;

        /* Initialize the name of the system thread (since CLDC 1.1) 设置线程名称为Thread-0*/
        javaThread->name = createCharArray("Thread-0", 8, &unused, FALSE);

        // 2. 构建Thread
        MainThread = BuildThread(&javaThread);

        /* AllThreads is initialized to NULL by the garbage collector.
         *
         * Ensure that the thread list is properly initialized
         * and set mainThread as the active (CurrentThread) thread
         * 将MainThread 设为活动线程
         */
        MainThread->nextThread = NIL;
        AliveThreadCount = 1;
        Timeslice = BASETIMESLICE;
        MainThread->state = THREAD_ACTIVE;

        /* Initialize VM registers 初始化寄存器 sp,fp,ip */
        CurrentThread = MainThread;
        RunnableThreads = NULL;
        TimerQueue = NULL;

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

        /* We can't create a frame consisting of "method", since its class
         * has not yet been initialized, (and this would mess up the
         * garbage collector).  So we set up a pseudo-frame, and arrange
         * for the interpreter to do the work for us.
         * 建立栈帧
         */
        pushFrame(RunCustomCodeMethod);
        // *(CustomCodeCallbackFunction *)(++(GlobalState.gs_sp)) = (initInitialThreadBehaviorFromThread);
        pushStackAsType(CustomCodeCallbackFunction,
                        initInitialThreadBehaviorFromThread);
        /* We want to push method, but that would confuse the GC, since
         * method is a pointer into the middle of a heap object.  Only
         * heap objects, and things that are clearly not on the heap, can
         * be pushed onto the stack.
         */
        //  *(INSTANCE_CLASS *)(++(GlobalState.gs_sp)) = (mainClass);
        pushStackAsType(INSTANCE_CLASS, mainClass);
        // *(ARRAY *)(++(GlobalState.gs_sp)) = (arguments);
        pushStackAsType(ARRAY, arguments);

        // 初始化class
        initializeClass(mainClass);
    END_TEMPORARY_ROOTS
}
  1. 实例化javaThread
  2. 构建Thread
  3. 将MainThread 设为活动线程
  4. 初始化寄存器 sp,fp,ip
  5. 建立栈帧
  6. 初始化class

这里比较重要的是第2,5,6步.

构建Thread

此处的代码如下:

static THREAD BuildThread(JAVATHREAD_HANDLE javaThreadH)
{
    THREAD newThread;
    JAVATHREAD javaThread;
    START_TEMPORARY_ROOTS
	     /*
	      * THREAD newThreadX = (
        newThreadX = (THREAD)callocObject(((sizeof(struct threadQueue) + 3) >> 2), GCT_THREAD),
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&newThreadX,
        newThreadX);
	      */
	    // 1. 在内存中分配THREAD,stack
        DECLARE_TEMPORARY_ROOT(THREAD, newThreadX,
                               (THREAD)callocObject(SIZEOF_THREAD, GCT_THREAD));
        STACK newStack = (STACK)callocObject(sizeof(struct stackStruct)
                                                     / CELL, GCT_EXECSTACK);
        /* newStack->next = NULL;  Already zero from calloc */
        newStack->size = STACKCHUNKSIZE; // 128
        newThreadX->stack = newStack;

#if INCLUDEDEBUGCODE
        if (tracethreading) {
            TraceThread(newThreadX, "Created");
        }
        if (tracestackchunks) {
            fprintf(stdout,
                "Created a new stack (thread: %lx, first chunk: %lx, chunk size: %ld\n",
                (long)newThreadX, (long)newStack, (long)STACKCHUNKSIZE);
        }
#endif /* INCLUDEDEBUGCODE */

        newThread = newThreadX;
    END_TEMPORARY_ROOTS

    /* Time slice will be initialized to default value */
    newThread->timeslice = BASETIMESLICE; // 1000

    /* Link the THREAD to the JAVATHREAD 2. 设置THREAD,JAVATHREAD的关联关系 */
    javaThread = unhand(javaThreadH);
    newThread->javaThread = javaThread;
    javaThread->VMthread = newThread;

    /* Initialize the state 3.设置状态 */
    newThread->state = THREAD_JUST_BORN;
#if ENABLE_JAVA_DEBUGGER
    newThread->nextOpcode = NOP;
#endif

    /* Add to the alive thread list  4.将vmthread添加到 AllThreads 中 */
    newThread->nextAliveThread = AllThreads;
    AllThreads = newThread;

    return(newThread);
}

该方法中的步骤如下:

  1. 在内存中分配THREAD,stack
  2. 设置THREAD,JAVATHREAD的关联关系
  3. 设置状态
  4. 将vmthread添加到AllThreads中

此处的代码比较简单,相关方法在之前文章中都有介绍.但是此处有个BASETIMESLICE需要提一下,这个宏的值决定了虚拟机执行线程切换、事件通知和一些其他定期需要的操作的基本频率(作为执行的字节码数)。较小的数目可以减少事件处理和线程切换延迟,但会导致解释器运行较慢。

此时如图所示:
在这里插入图片描述

pushFrame

此处的代码如下:

void pushFrame(METHOD thisMethod)
{
    int thisFrameSize = thisMethod->frameSize;
    int thisArgCount = thisMethod->argCount;
    int thisLocalCount = thisFrameSize - thisArgCount; // 计算局部变量的数量
    
    STACK stack = getFP() ? getFP()->stack : CurrentThread->stack; // 获得栈
    // 该方法所需要的大小(字节为单位)
    int thisMethodHeight = thisLocalCount + thisMethod->u.java.maxStack + // 操作数栈的部分
                           SIZEOF_FRAME /* 存储frameStruct结构 */ + RESERVEDFORNATIVE; //RESERVEDFORNATIVE = 为了本地方法而保留的3个cell
    FRAME newFrame;
    int i;
    cell* prev_sp = getSP() - thisArgCount; /* Very volatile! 获得之前的sp*/
    
    /*  计算在目前的stack chunk 中新增一个frame 结构是否会不会超过该chunk的容量*/
    if (getSP() - stack->cells + thisMethodHeight >= stack->size) {
        
        /* 需要重新创建一个stack chunk*/
        STACK newstack;
        thisMethodHeight += thisArgCount;

        /* 如果存在下一个stack但是stack的size不够用,则设置 stack->next = NULL
         * 而之前的stack->next由于没有任何指针指向,因此会被下一次的gc所释放掉 */
        if (stack->next && thisMethodHeight > stack->next->size) {
            stack->next = NULL;
        }

        /* 创建stack */
        if (stack->next == NULL) {
            int size = thisMethodHeight > STACKCHUNKSIZE 
                     ? thisMethodHeight : STACKCHUNKSIZE;
            int stacksize = sizeof(struct stackStruct) / CELL + 
                           (size - STACKCHUNKSIZE);
            START_TEMPORARY_ROOTS
			   /**
			    * STACK stackX = (
        stackX = stack,
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&stackX,
        stackX);
			    */
                DECLARE_TEMPORARY_ROOT(STACK, stackX, stack);
                newstack = (STACK)mallocHeapObject(stacksize, GCT_EXECSTACK); 
                stack = stackX;
                prev_sp = getSP() - thisArgCount;
            END_TEMPORARY_ROOTS
            if (newstack == NULL) {
                THROW(StackOverflowObject);
            }

#if INCLUDEDEBUGCODE
            /* In debug mode, initialize the new stack chunk to zeros */
            memset(newstack, 0, stacksize << log2CELL);
#endif

            newstack->next = NULL;
            newstack->size = size;
            stack->next = newstack;

#if INCLUDEDEBUGCODE
            if (traceframes || tracestackchunks) {
                fprintf(stdout,
                    "Created a new stack chunk (thread: %lx, new chunk: %lx, prev: %lx, stack depth: %ld)\n", 
                    (long)CurrentThread, (long)newstack,
                    (long)stack, (long)frameDepth());
            }
#endif
        } else {
            /* Can reuse an existing, unused stack chunk */
            newstack = stack->next;
        }

        /*  参数复制 */
        for (i = 0; i < thisArgCount; i++) {
            newstack->cells[i] = prev_sp[i + 1];
        }
        setLP(newstack->cells);
        newFrame = (FRAME)(getLP() + thisFrameSize);
        newFrame->stack = newstack;
    } else {

        
        /* 所剩余的空间足够再存放一个新的frame结构 */
        ASSERTING_NO_ALLOCATION
            /* Set the local variable pointer to point to the start */
            /* of the local variables in the execution stack */
            setLP(prev_sp + 1);

           
            newFrame = (FRAME)(getSP() + thisLocalCount + 1);
            newFrame->stack = stack;
        END_ASSERTING_NO_ALLOCATION
    }

#if ENABLE_JAVA_DEBUGGER
    /*
     * Although the GC doesn't need to zero out the locations, the debugger
     * code needs to have unallocated objects zeroed out on the stack, else
     * it will try to dereference them when the debugger asks for the local
     * variables.
     */
    if (vmDebugReady)
        memset(getLP() + thisArgCount, 0, thisLocalCount << log2CELL);
#endif

    /* Fill out the remaining fields in the stack frame */
    ASSERTING_NO_ALLOCATION
        /* Initialize info needed for popping the stack frame later on */
        newFrame->previousSp = prev_sp;
        newFrame->previousIp = getIP();
        newFrame->previousFp = getFP();

        /* Initialize the frame to execute the given method */
        newFrame->thisMethod = thisMethod;
        newFrame->syncObject = NIL; /* Initialized later if necessary */

        /* Change virtual machine registers to execute the new method */
        setFP(newFrame);
        // 由于newFrame 现在指向FRAME结构的低端,而且其类型为指向FRAME的指针,所以把newFrame + 1 会得到一个指向FRAME后的第一个内存指针的指针
        // 然后,把方才得到的指针强转成cell的指针后,减一可以得到一个指向FRAME结构顶端的指针,并赋值为sp
        setSP((cell*)(newFrame + 1) - 1);
        setIP(thisMethod->u.java.code);
        setCP(thisMethod->ofClass->constPool);

#if INCLUDEDEBUGCODE
        if (tracemethodcalls || tracemethodcallsverbose) {
            frameTracing(thisMethod, "=>", 0);
        }

        if (traceframes) {
            fprintf(stdout, 
                "Pushed a stack frame (thread: %lx, fp: %lx, sp: %lx, depth: %ld, stack: %lx)\n",
                (long)CurrentThread, (long)getFP(),
                (long)getSP(), (long)frameDepth(), (long)stack);
        }
#endif /* INCLUDEDEBUGCODE */

    END_ASSERTING_NO_ALLOCATION
}

在kvm内部定义了如下几个变量:

#define ip_global GlobalState.gs_ip
#define sp_global GlobalState.gs_sp
#define lp_global GlobalState.gs_lp
#define fp_global GlobalState.gs_fp
#define cp_global GlobalState.gs_cp

struct GlobalStateStruct { 
    BYTE*         gs_ip; /*  指向目前kvm正在执行method的Instruction*/
    cell*         gs_sp; /*  指向目前kvm正在执行method的 操作stack*/
    cell*         gs_lp; /*  指向目前kvm正在执行method的本地变量*/
    FRAME         gs_fp; /*  指向目前kvm正在执行method的 frame*/
    CONSTANTPOOL  gs_cp; /*  指向目前kvm正在执行method所属class的 运行时常量池*/
};

这部分的关系如图:

在这里插入图片描述

首先先看下面这段代码:

cell* prev_sp = getSP() - thisArgCount; /* Very volatile! 获得之前的sp*/

不管在kvm还是在jvm,其两个方法之间的栈帧有一部分是重叠的,其目标是减少参数的复制.以上代码的情况如下所示:

在这里插入图片描述

其他的结合代码注释和以上2图,可以很清楚的理解,这里就不做过多解释.

pushStackAsType

代码如下:

pushStackAsType(CustomCodeCallbackFunction,
                        initInitialThreadBehaviorFromThread);

#define pushStackAsType(_type_, data)   *(_type_ *)(++getSP()) = (data)                       

通过看上图可以知道,sp是指向操作数栈的低部,那么此处通过push就需要增加sp指针.这很好理解.

initializeClass

初始化类.代码如下:

void initializeClass(INSTANCE_CLASS thisClass)
{
    if (thisClass->status == CLASS_ERROR) {
        raiseException(NoClassDefFoundError);
    } else if (thisClass->status < CLASS_READY) {
        if (thisClass->status < CLASS_VERIFIED) {
            verifyClass(thisClass); // 验证class
        }
        /*
         * VerifyError will have been thrown or status will be
         * CLASS_VERIFIED. We can skip execution of <clinit> altogether if
         * it does not exists AND the superclass is already initialised.
         */
        if ((thisClass->superClass == NULL ||
            thisClass->superClass->status == CLASS_READY) &&
            getSpecialMethod(thisClass,clinitNameAndType) == NULL) {
            setClassStatus(thisClass,CLASS_READY);
        }
        else {
            TRY {
                pushFrame(RunCustomCodeMethod);
                pushStackAsType(CustomCodeCallbackFunction, &runClinit);
                pushStackAsType(INSTANCE_CLASS, thisClass);
                pushStackAsType(long, 1);
            } CATCH (e) {
                /* Stack overflow */
                setClassStatus(thisClass, CLASS_ERROR);
                THROW(e);
            } END_CATCH
        }
    }
}

此处可以分为两部分.

  1. 验证class ,这部分可以参考 kvm 验证-模拟字节码执行,kvm类加载-007,StackMap属性解析
  2. 操作栈帧. 这部分上文有介绍

初始化系统类

此处的代码如下:

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

总结

通过本文的一系列的操作,则此时的栈帧如下:

在这里插入图片描述

可以看到其依次初始化class,thread,String,System,OutOfMemoryError,应用主类,然后执行主类的main方法.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值