本文介绍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. 构建ThreadMainThread = 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);// 初始化classinitializeClass(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,stackDECLARE_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; // 128newThreadX->stack = newStack;#if INCLUDEDEBUGCODEif (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_DEBUGGERnewThread->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个cellFRAME 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_ROOTSif (newstack == NULL) {THROW(StackOverflowObject);}#if INCLUDEDEBUGCODE/* In debug mode, initialize the new stack chunk to zeros */memset(newstack, 0, stacksize << log2CELL);
#endifnewstack->next = NULL;newstack->size = size;stack->next = newstack;#if INCLUDEDEBUGCODEif (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结构顶端的指针,并赋值为spsetSP((cell*)(newFrame + 1) - 1);setIP(thisMethod->u.java.code);setCP(thisMethod->ofClass->constPool);#if INCLUDEDEBUGCODEif (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_cpstruct 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方法.

kvm启动流程-006相关推荐

  1. Hyperledger Fabric笔记3--BYFN启动流程分析

    Hyperledger Fabric笔记3--BYFN启动流程分析 BYFN--构建你的第一个网络,该方案提供了一个示例Hyperledger Fabric网络,该网络由两个组织组成,每个组织都维护两 ...

  2. 源码分析-Activity的启动流程

    以android 6.0源码为参考,其他版本api会稍有不同 在Activity中,启动一个Activity的方法 @Override public void startActivity(Intent ...

  3. Centos 6启动流程详解

    author:JevonWei 版权声明:原创作品 Centos6 启动流程 POST开机自检 当按下电源键后,会启动ROM芯片中的CMOS程序检查CPU.内存等硬件设备是否正常运行,CMOS中的程序 ...

  4. 2014.4新版uboot启动流程分析

    原文 http://blog.csdn.net/skyflying2012/article/details/25804209 此处转载有稍作修改 最近开始接触uboot,现在需要将2014.4版本ub ...

  5. 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  6. leveldb源码分析:Open启动流程

    leveldb概述 Leveldb 是一个持久化的KV存储系统,主要将大部分数据存储在磁盘上,在存储数据的过程中,根据记录的key值有序存储,当然使用者也可以自定义Key大小比较函数,一个leveld ...

  7. Nginx源码分析:启动流程

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...

  8. JVM启动流程和内存结构

    "Great haste makes great waste" JVM启动流程 JVM是Java程序运行的环境,同时是一个操作系统的一个应用程序进程,因此它有自己的生命周期,也有自 ...

  9. mbr,gpt,开机启动流程.

    启动流程 SystemV 加载 BIOS 的硬件资讯与进行自我测试,并依据配置取得第一个可启动的装置: 读取并运行第一个启动装置内 MBR 的 boot Loader (亦即是 grub, spfdi ...

最新文章

  1. php显示服务器文件,php-无法显示从新服务器下载文件的进度(在以前的服务器上工作)...
  2. Eclipse插件开发中File和IFile的转换
  3. String比较? 用==判断两个字符串,有时为true有时为false
  4. python 函数式 panda_python – 反向中的Pandas分裂函数
  5. 数据库-多条件查询-优先级
  6. WebStorm使用TypeScript
  7. 计算机体系结构----常见英文缩写(待更....)
  8. Java多线程系列(十):源码剖析AQS的实现原理
  9. android.view.WindowManager$BadTokenException
  10. Java 实现计时器
  11. [转]为什么mysql默认隔离级别设置为可重复读
  12. 1.Head First Java --- 进入Java的世界
  13. php函数 call_user_func
  14. std::sort使用
  15. python遍历json_Python学习笔记:Python3中Json数据遍历取指定值
  16. 3. Git与TortoiseGit基本操作
  17. readxmls r语言_R语言批量爬取NCBI基因注释数据
  18. res.data, res.data.data, res.data.data.rows的含义
  19. 【开源分享】多端发布的单商户商城系统
  20. 从字节码指令分析i=i++(i是long类型时)

热门文章

  1. 智慧之战——农民工大战博士后
  2. 阿里云Redis开发遇到的问题总结
  3. 短信验证码总是发送失败是什么原因?
  4. 人重要的是知耻而后勇
  5. linux----------2--3----(无名)管道通信原理及管道编程实战
  6. 深入理解计算机系统2——信息表示和处理
  7. R语言绘制箱体图举例图文版
  8. 移动端开发-体检预约
  9. [校内自测] Incr (LIS+智商)
  10. RuntimeError: Attempting to deserialize object on CUDA device 2 but torch.cuda.device_count() is 2.