用Go编写JVM之搜索class文件

x_zhaohu · · 1097 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

源于只因遇见Go,被Go的魅力所折服,慢慢将发生下面的故事

Java类加载机制

Java类加载器的作用,将class文件加载到内存。从Java开发人员的角度来说,类加载器可分为三种:

  • 启动类加载器(Bootstrap ClassLoader):加载<JAVA_HOME>/jre/lib下的jar
  • 扩展类加载器(Extension ClassLoader):加载<JAVA_HOME>/jre/lib/ext/下的jar
  • 应用程序类加载器(Application ClassLoader):加载用户自己编写的class

大多数的加载器使用双亲委派模型,双亲委派模型的工作过程是: 如果一个类加载器收到了类加载器的请求,它首先不会自己去尝试加载这类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。


类加载流程图.jpg

实现思路

  • 设置命令行参数,指定路径
  • 实现类路径:启动类加载器,扩展类加载器,应用程序类加载器
  • 将类路径抽象出来,这三个作为其子类

编码过程

编码过程从三方面介绍,首先是工程目录,让读者可以清晰看见。其次是运行效果,读者可根据实现的搜索class文件的效果如何,选择是否继续往下观看。源码请移步传送门

  • 工程目录
  • 运行效果
  • 代码实现

工程目录

  • gopath:代码存放区(workspace)
  • gopath/src:存放源码的区域
  • jvmgo:项目名
  • ch02:子文件
  • ch02/classpath/:这次主要编写的代码

project02.png

运行效果

ch02/下编译: go build -o readClass 后所在当前目录下便生成可执行文件readClass

验证通过启动类或扩展类加载器加载java.lang.Object-Xjre是预定义的命令行参数,"/home/sprint/java/utils/jdk1.8.0_91/jre"指定jre路径,效果图如下:


02_result_01.png

验证通过应用程序类加载器加载ch02/Testclass(自己编写的类),如果不指定路径,默认当前路径。-cp是预定义的命令行参数,""代表当前目录,效果图如下:


02_result_02.png

代码实现

  • terminal.go中预定义Xjre命令行参数
//定义Termial结构体
type Terminal struct {
    //...省略字段,
    XjreOption  string // 添加XjreOption字段
}

func parseTerminal() *Terminal {
    terminal := &Terminal{}
    //省略部分代码...
    flag.StringVar(&terminal.XjreOption, "Xjre", "", "path to jre")
    flag.Parse()
    //省略部分代码...
    return terminal
}
  • 编写类路径及其子类
    总类路径包含:启动类加载器路径,扩展类加载器路径,应用程序类加载器路径。在ch02/classpath/下编写classpath.go,定义类路径如下:

    type Classpath struct {
      //启动类加载器
      bootClasspath Entry
      //扩展类加载器
      extClasspath Entry
      //应用程序加载器
      userClasspath Entry
    }

    每种类加载器都有相同的方法,这时定义一个接口开表示类路径项。在ch02/classpath/下编写entry.go文件定义Entry接口:

    type Entry interface {
      //负责寻找和加载class
      readClass(className string) ([]byte, Entry, error) //Go函数或方法运行返回多个值
    
      //类似toString()
      String() string
    }

    DirEntry代表:应用程序类加载器,用于加载文件目录中的class。ZipEntry代表:应用启动类和扩展类加载器,用于加载jar/zip文件。CompositeEntryWildcardEntry代表:Entry的集合。在ch02/classpath下创建entry_dir.go, entry_zip.go,entry_composite.go,entry_wildcard.go文件实现Entry接口。Go的实现接口的方式与Java有所不同,想体验的话,动手实现下,体验下Go的魅力吧!由于考虑篇幅,所以接口具体实现不贴代码了,具体源码请移步传送门

  • 测试执行
    编写完上述代码后,更改ch02/main.go中的startJVM()函数来进行测试。
func startJVM(terminal *Terminal) {
    cp := classpath.Parse(terminal.XjreOption, terminal.cpOption)
    fmt.Printf("classpath:%v class:%v args:%v\n",
        cp, terminal.class, terminal.args)
    className := strings.Replace(terminal.class, ".", "/", -1)
    classData, _, err := cp.ReadClass(className)
    if err != nil {
        fmt.Printf("Could not find or load main class %s\n", terminal.class)
    }
    fmt.Printf("class data:%v\n", classData)
}

一切准备就绪后,在ch02文件下敲入go build -o readClass,便生成了readClass可执行文件。之后按照"运行效果图"中命令行进行执行测试吧!

小结与声明:一边进一步理解Java虚拟机,一边学习Go。知识主要来源于gitbook(下面有地址)和张秀宏老师的《自己动手编写Java虚拟机》,我的源代码与书上的源码稍微有些不同。

参考文献


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:x_zhaohu

查看原文:用Go编写JVM之搜索class文件

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1097 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传