bboyjing's blog

自己动手写JVM一【准备环境】

之前看过两遍《深入理解java虚拟机》,能吸收点知识,但是远远没有能够深入理解。最近购入张秀宏著的《自己动手写Java虚拟机》一书,抱着想要更深地理解JVM的目的去学习,并记录下学习笔记。第一章节还是先来准备环境。

Java

关于Java环境的准备步骤就不赘述了,本人使用的版本是1.8.0_91

Go

这本书的JVM实现是用Go写的,之前没有接触过,在这一学习过程中顺带就把Go也简单地学习下了。Go的安装参照golang,选择的是目前最新版是1.7.4。Go的参考资料选择Go语言圣经Go语言指南

Go安装步骤:

  1. 下载对应版本的安装包,本人系统时Ubuntu,所以选择的是go1.7.4.linux-amd64.tar.gz
  2. 解压到安装目录

    1
    sudo tar -C /usr/local -xvf ~/Downloads/go1.7.4.linux-amd64.tar.gz
  3. 添加环境变量,各个系统配置环境变量的位置不一样,具体自行查看

    1
    2
    3
    4
    #添加Go的运行环境路径
    export PATH=$PATH:/usr/local/go/bin
    #添加Go工程的工作空间,下面是本人的工作空间,可自行修改
    export GOPATH=/home/zhangjing/IdeaProjects/jvmgo/go
  4. 测试Go环境是否安装成功

    1
    2
    ~ go version
    go version go1.7.4 linux/amd64

Go环境安装完毕,编辑器本人还是选用Intellij IDEA,安装个Go插件,直接在线安装即可,顺便附上插件地址。安装完毕之后,新建个项目,放到在$GOPATH目录下。Go的源文件位于src目录下,Go语言的代码是通过package来组织的,可以在src下建自己想要的包名。下面建个Go文件测试下:
jvmgo_1
根据上述图片可以看出,项目放在$GOPATH目录下,包和go源文件都位于src目录下。这里再稍微讲解下helloworld.go文件,package main是一个比较特殊的package。这个package里会定义一个独立的程序,这个程序是可以运行的,而不是像其它package一样对应一个library。在main这个package里,main函数也是一个特殊的函数,这是我们整个程序的入口,所以helloworld.go是个可运行go文件。在idea中可以直接右键运行程序,这个没啥好说的。但还是有必要了解下Go提供的命令行工具:

  • 直接运行

    1
    2
    3
    cd /home/zhangjing/IdeaProjects/jvmgo/go
    #go run将运行go文件
    go run src/cn.didadu/hello/helloworld.go
  • 先编译再运行

    1
    2
    3
    4
    5
    cd /home/zhangjing/IdeaProjects/jvmgo/go
    #生成可执行文件
    go install cn.didadu/hello
    #生成类库
    go install cn.didadu/jvmgo

    要注意下go install命令,目录只需要从src的子目录开始,目标也是目录不是文件,执行install命令的时候应该是从$GOPATH下的src目录开始查找给定的包。这样的话,第一行的cd命令就是多于的了。go install命令执行之后会在$GOPATH目录下新建bin目录,和src同级,会把helloworld.go编译成和包名一致的可执行文件(hello)放到到bin目录中。这个bin目录也就是go工程放置可执行文件的目录了。另外会看到还有一个pkg目录,该目录是放置非可执行文件的类库目录。
    jvmgo_2

至此,环境准备工作就做好了,下面就可以开始学习了。示例代码位于本人gitbub仓库jvmgo项目下。

实现java命令

Java环境安装完毕,我们通常都会在命令行输入java -version来测试下是否安装成功。java -verson就是Java提供的命令功能,而本节就来实现这一功能,代码位于cmd包下。下面先列出一些java命令常用的选项以及用途:

选项 用途
-version 输出版本信息,然后退出
-?/-help 输出帮助信息,然后退出
-cp/-classpath 指定用户类路径
-Dproperty=value 设置Java系统属性
-Xms 设置初始堆空间大小
-Xmx 设置最大堆空间大小
-Xss 设置线程栈空间大小

说明一下,本人也是初次使用Go语言,有使用不当的地方还请谅解,而且可能会使用比较多的注释来说明下Go语言语法。
下面将编写开始编写简单的java命令行程序,核心代码位于cmd包下的cmd.go文件中。其中用到了Go内置的fmt、os、flag包,先稍微了解下这三个包:

  • fmt:格式化输入输出
  • os:提供了操作系统无关(跨平台)的,与系统交互的一些函数和相关的变量
  • flag:处理命令行参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package cmd
import (
"flag"
"fmt"
"os"
)
/*
定义结构体,用于存储命令参数。
java [-options] class [args...]
*/
type Cmd struct {
HelpFlag bool
VersionFlag bool
CpOption string
Class string
Args []string
}
/*
ParseCmd()方法返回值为*Cmd,是指向Cmd的值的指针。
Go语言中,常量、函数的首字母大写表示对外公开的相当于Java的public,小写表示私有的相当于Java的private。
*/
func ParseCmd() *Cmd {
//声明cmd为指向空的Cmd对象的指针
cmd := &Cmd{}
/*
Usage是一个函数,默认输出所有定义了的命令行参数和帮助信息
一般,当命令行参数解析出错时,该函数会被调用
这里我们指定了自己的Usage函数,即printUsage()
*/
flag.Usage = PrintUsage
//flag.XxxVar(),将flag绑定到第一个参数指定的指针上,并设置默认值和提示信息
flag.BoolVar(&cmd.HelpFlag, "help", false, "print help message")
flag.BoolVar(&cmd.HelpFlag, "?", false, "print help message")
flag.BoolVar(&cmd.VersionFlag, "version", false, "print version and exit")
flag.StringVar(&cmd.CpOption, "classpath", "", "classpath")
flag.StringVar(&cmd.CpOption, "cp", "", "classpath")
//在所有的flag定义完成之后,可以通过调用flag.Parse()进行解析。
flag.Parse()
args := flag.Args()
if len(args) > 0 {
cmd.Class = args[0]
cmd.Args = args[1:]
}
return cmd
}
// 提示输入正确的命令格式
func PrintUsage() {
fmt.Printf("Usage: %s [-options] class [args...]\n", os.Args[0])
}

测试代码位于jvm包下的main.go文件中,本章节以及后面的所有功能都将使用这一个测试入口,这一节特别说一下,后面就不说了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import (
"cn.didadu/jvmgo/cmd"
"fmt"
)
func main() {
command := cmd.ParseCmd()
if command.VersionFlag {
fmt.Println("version 0.0.1")
} else if command.HelpFlag || command.Class == "" {
cmd.PrintUsage()
} else {
startJVM(command)
}
}
// 模拟启动JVM
func startJVM(cmd *cmd.Cmd) {
fmt.Printf("classpath:%s class:%s args:%v\n",
cmd.CpOption, cmd.Class, cmd.Args)
}

编译main.go,并测试-version

1
2
3
4
5
cd /home/zhangjing/IdeaProjects/jvmgo/go
go install cn.didadu/jvmgo
./bin/jvmgo -version
#输出如下信息
version 0.0.1

这一章节就写到这里,实现了一个简化的命令行工具,主要的还是接触了一门新的语言Go,要学的东西还很多,继续努力吧。