windows系统使用c++实现一个小型jvm(三)------------jvm的启动细节2

网友投稿 594 2022-11-03

windows系统使用c++实现一个小型jvm(三)------------jvm的启动细节2

windows系统使用c++实现一个小型jvm(三)------------jvm的启动细节2

这篇文章接着上午记录下。

1.标准输入输出流是怎么来的?

在写java程序的时候,我们经常输出控制台信息,调用的如下代码: System.out.Println();  在这里,我将解释这个的由来。

jvm在初始化时,必须先加载FileDescriptor,FileDescriptor有三个静态成员:

它们会调用本地FileDescriptor的SetI方法:

void JVM_FD_Set(list& _stack){ //todo: 这里是初始化FileDescriptors时,会将标准输入输出,以及错误流 进行与FileOutputStream绑定 IntOop *fd = (IntOop *)_stack.front(); _stack.pop_front(); HANDLE ret; if(fd->value==0){//标准输入流 ret= GetStdHandle(STD_INPUT_HANDLE); }else if(fd->value==1){//标准输出流 ret= GetStdHandle(STD_OUTPUT_HANDLE); }else{//标准错误流 ret= GetStdHandle(STD_ERROR_HANDLE); } long addr = HandleToLong(ret);//ret为指针类型,该指针类型指向long值,*为取出该值 _stack.push_back(new LongOop(addr));}

windows中,每个程序的标准输入输出流和错误流,为当前程序的控制台。  这里实际上就是控制台程序的句柄给设置到相应的对象中。  之后再初始化System的时候,会有如下的代码片段:

而实际上这个setIn0,setOut0,setErr0,就是将输入输出流句柄,给设置到 System.in,或者System.out,System.error属性上去,其代码如下:

void JVM_SetOut0(list & _stack){ // static InstanceOop *printstream = (InstanceOop *)_stack.front(); _stack.pop_front(); auto system = BootStrapClassLoader::get_bootstrap().loadClass(L"java/lang/System"); assert(system != nullptr); ((InstanceKlass *)system)->set_static_field_value(L"out:Ljava/io/PrintStream;", printstream);}

另外,从这个流程中,我想起来大概两年前写 看java源码的 流 部分的时候,曾经总结过,说: java中真正更够读写的就两个流,一个是FileInputStream/FileOutputStream ,另一个是ArrayIntputStream/ArrayOutputStream。   知识诚不欺我呀哈哈哈哈。

2.java的双亲类加载机制:

说到这个古老的话题,那可得追溯到我写文章开始。 那一年春节,我决定看看源码,一上来就猛的 想把那个双亲加载机制给搞明白。 事实上,一直没怎么搞明白,唯一的作用是给了自己学习的动力。  但是今天我想借着这个机会,是可以完全搞懂的。

上午说到,jvm在加载客户类之前,会启动一个LauncherHelper类,代码如下:

BytecodeEngine::initial_client(launcher_helper_klass, *this); Method *load_main_method = launcher_helper_klass->get_this_class_method(L"checkAndLoadMain:(ZILjava/lang/String;)Ljava/lang/Class;"); // new a String. wstring ss = automan_jvm::main_class_name(); InstanceOop *main_klass = (InstanceOop *)java_lang_string::intern(automan_jvm::main_class_name()); this->vm_stack.push_back(StackFrame(load_main_method, nullptr, nullptr, {new IntOop(true), new IntOop(1), main_klass}, this)); //todo: 到这里应该是java层面的类加载器开始生效!!! MirrorOop *main_class_mirror = (MirrorOop *)this->execute();

接着去看看 checkAndLoadMain方法:

之后通过ClassLoader的loadClass方法,通过查找虚拟表,调用Launcher的AppClassLoader的loadClass方法,再调用之前,将会首先进行AppClassClassloader的初始化(这个初始化过程蛮复杂的):

花了较多的精力去看这块的源码,主要原因基于以下几点:

1.windows中由于文件系统路径分隔符的原因,我在调试时就遇到了一个坑,即我明明需要使用 文件系统去加载,但是它却使用了JarLoader去加载。   现在回过头来,知道了其原因: 首先在LauncherHelper中会根据模式选择加载器,其次会判断java.class.path下面的所有配置路径,如果为文件夹,则会匹配为 fileLoader,如果为文件,则会使用默认的loader,而实际上默认的loader,其最终采用的还是Jarloader的方式加载的。(我的问题就出在这!)。

2.AppClassLoader与ExtClassLoader都继承自URLClasspath,因此必须弄明白URLClasspath是在什么时机初始化的,以及相应的参数都是什么。  关于URLClasspath的作用,网上帖子挺多。 它的几个关键方法: loadClass,findClass,defineClass 可以用于验证双亲委派机制的运行流程。

我在调试时,分别给loadClass 和  defineClass添加了锚点, 最终实际上是可以印证双亲委派模型的。  由于当时未截图,因此这里就不再继续操作了。(因为这个过程实在是有些痛苦~,感兴趣的小伙伴可以亲自调试一下)。

另外强调一点就是,以前没有认识到URLClassLoader在java中的重要性,以及URLClassLoader与URLClasspath的关系,这是一个十分有趣的问题。

此外,在AppclassLoader加载类的过程中,它的流将会以匿名内部类的形式给出:

就到这吧,原计划写的很详细的,但是真写了太细了吧,速度太慢,自己又是个急性子。  whatever,后面赶紧把gc写了要开始投入新一轮的学习,同时得计划找工作了55555。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:CastleCSS是一个移动优先的SCSS框架拥有模块化的构建块
下一篇:【IDEA插件】为插件化框架 Small 建立页面索引和快速跳转
相关文章

 发表评论

暂时没有评论,来抢沙发吧~