{"id":20917562,"url":"https://github.com/impact-eintr/jvmgo","last_synced_at":"2026-04-23T15:37:55.523Z","repository":{"id":59132024,"uuid":"494712001","full_name":"impact-eintr/jvmgo","owner":"impact-eintr","description":"jvm implement by golang","archived":false,"fork":false,"pushed_at":"2022-11-16T02:00:59.000Z","size":1425,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-19T16:48:18.637Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/impact-eintr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-21T07:27:57.000Z","updated_at":"2024-01-14T10:34:44.000Z","dependencies_parsed_at":"2023-01-23T08:01:06.545Z","dependency_job_id":null,"html_url":"https://github.com/impact-eintr/jvmgo","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impact-eintr%2Fjvmgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impact-eintr%2Fjvmgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impact-eintr%2Fjvmgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/impact-eintr%2Fjvmgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/impact-eintr","download_url":"https://codeload.github.com/impact-eintr/jvmgo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243312039,"owners_count":20271095,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-18T16:34:12.370Z","updated_at":"2025-12-26T16:03:24.436Z","avatar_url":"https://github.com/impact-eintr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jvmgo\njvm implement by golang\n\n在加载一个类之前要先加载它的超类，也就是java.lang.Object\n\n多线程共享的内存区域主要存放两种数据：类数据和类实例（对象）。对象数据存放在Heap中，类数据存放在方法区中。堆由垃圾收集器定时清理，类数据包括字段和方法信息、方法的字节码、运行时常量池等等。从逻辑上讲，其实方法区也是堆的一部分。\n\n线程私有的运行是数据区用于辅助执行java字节码。每个线程都有自己的pc寄存器和java虚拟机栈。\n\nJVM Stack 又由 Stack Frame 构成，栈帧中保存方法执行的状态，包括局部变量表和操作数栈(Operand Stack)等。在任一时刻，某一线程肯定是在执行某个方法。这个方法叫做该线程的当前方法；执行该方法的帧叫做线程的当前帧；声明该方法的类叫做当前类。\n\n如果当前方法是java方法。则pc寄存器中存发当前正在执行的java虚拟机指令的地址，否则，当前方法是本地方法，pc寄存器中的值没有明确定义。\n\n\nJava虚拟机规范对于运行时数据区的规定是相当宽松的。以堆为例:堆可以是连续空间,也可以不连续。堆的大小可以固定,也可以在运行时按需扩展。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾收集也是可以的。由于Go本身也有垃圾回收功能,所以可以直接使用Go的堆和垃圾收集器,这大大简化了我们的工作。\n\nJava虚拟机可以操作两类数据:基本类型(primitive type)和引用类型(reference type)。基本类型的变量存放的就是数据本身,引用类型的变量存放的是对象引用,真正的对象数据是在堆里分配的。这里所说的变量包括类变量(静态字段)、实例变量(非静态字段)、数组元素、方法的参数和局部变量,等等。\n\n\n\n基本类型可以进一步分为布尔类型(boolean type)和数字类型(numeric type),数字类型又可以分为整数类型(integral type)和浮点数类型(floating-point type)。引用类型可以进一步分为3种:类类型、接口类型和数组类型。类类型引用指向类实例,数组类型引用指向数组实例,接口类型引用指向实现了该接口的类或数组实例。引用类型有一个特殊的值——null,表示该引用不指向任何对象。\n\n接下来，我们按照函数的执行顺序追踪一下JVM的实现，我们需要向计算机一样压很多层栈。当然我会保存一些上下文，免得阅读时读到一半又要跳回来。\n\n代码详见[https://github.com/impact-eintr/jvmgo](https://github.com/impact-eintr/jvmgo)\n\n## 主函数\n\n``` go\nfunc main() {\n\tcmd := parseCmd()\n\tif cmd.versionFlag {\n\t\tfmt.Println(\"version: v0.0.1\")\n\t} else if cmd.helpFlag || cmd.class == \"\" {\n\t\tprintUsage()\n\t} else {\n\t\tnewJVM(cmd).start()\n\t}\n}\n```\n\n\n### 命令行解析\n\n``` go\ntype Cmd struct {\n\thelpFlag bool\n\tversionFlag bool\n\tverboseClassFlag bool\n\tverboseInstFlag bool\n\tcpOption string\n\tXjreOption string\n\tclass string\n\targs []string\n}\n```\n\n- 解析命令\n\n``` go\nfunc parseCmd() *Cmd {\n\tcmd := \u0026Cmd{}\n\n\tflag.Usage = printUsage\n\tflag.BoolVar(\u0026cmd.helpFlag, \"help\", false, \"print help message\")\n\tflag.BoolVar(\u0026cmd.helpFlag, \"?\", false, \"print help message\")\n\tflag.BoolVar(\u0026cmd.versionFlag, \"version\", false, \"print version and exit\")\n\tflag.BoolVar(\u0026cmd.verboseClassFlag, \"verbose:class\", false, \"enable verbose output\")\n\tflag.BoolVar(\u0026cmd.verboseInstFlag, \"verbose:inst\", false, \"enable verbose output\")\n\tflag.StringVar(\u0026cmd.cpOption, \"classpath\", \"\", \"classpath\")\n\tflag.StringVar(\u0026cmd.cpOption, \"cp\", \"\", \"classpath\")\n\tflag.StringVar(\u0026cmd.XjreOption, \"Xjre\", \"\", \"path to jre\")\n\tflag.Parse()\n\n\targs := flag.Args()\n\tif (len(args) \u003e 0) {\n\t\tcmd.class = args[0]\n\t\tcmd.args = args[1:]\n\t}\n\n\treturn cmd\n}\n```\n\n捕获对应的字符，绑定到Cmd的字段。\n\n\n### 构造虚拟机\n\n``` go\n\t\tnewJVM(cmd).start()\n```\n\n- 虚拟机对象\n\n``` go\ntype JVM struct {\n\tcmd *Cmd\n\tclassLoader *heap.ClassLoader\n\tmainThread *rtda.Thread\n}\n```\n\n1. cmd cli解析器\n2. classLoader 类加载器\n3. mainThread 运行时主线程\n\n- 构造JVM对象\n\n``` go\nfunc newJVM(cmd *Cmd) *JVM {\n\tcp := classpath.Parse(cmd.XjreOption, cmd.cpOption)\n\tclassLoader := heap.NewClassLoader(cp, cmd.verboseClassFlag)\n\treturn \u0026JVM{\n\t\tcmd: cmd,\n\t\tclassLoader: classLoader,\n\t\tmainThread: rtda.NewThread(),\n\t}\n}\n```\n\n\n#### classpath\n首先来看classpath对象\n\n``` go\ntype Classpath struct {\n\tbootClasspath Entry\n\textClasspath Entry\n\tuserClasspath Entry\n}\n```\n\n- `Entry` 是一个 interface\n\n``` go\ntype Entry interface {\n\treadClass(className string) ([]byte, Entry, error)\n\tString() string\n}\n```\n\n`readClass` 是不同类型classpath对应的读取class对象的实现方法。\n\n`String` 不同类型classpath对应的路径\n\n``` go\nfunc newEntry(path string) Entry {\n\tif strings.Contains(path, pathListSeparator) {\n\t\treturn newCompositeEntry(path)\n\t}\n\n\tif strings.HasSuffix(path, \"*\") {\n\t\treturn newWildcardEntry(path)\n\t}\n\n\tif strings.HasSuffix(path, \".jar\") || strings.HasSuffix(path, \".JAR\") ||\n\t\tstrings.HasSuffix(path, \".zip\") || strings.HasSuffix(path, \".ZIP\") {\n\t\treturn newZipEntry(path)\n\t}\n\n\treturn newDirEntry(path)\n}\n```\n\n可以看到有4种Entry\n1. WildcardEntry 路径中包含通配符*\n\n``` go\nfunc newWildcardEntry(path string) CompositeEntry {\n\tbaseDir := path[:len(path)-1]\n\tcompositeEntry := []Entry{}\n\n\t// 对某个路径下的所有文件执行此函数\n\twalkFn := func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.IsDir() \u0026\u0026 path != baseDir {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\t\t// 对jar包构建Entry\n\t\tif strings.HasSuffix(path, \".jar\") || strings.HasSuffix(path, \".JAR\") {\n\t\t\tjarEntry := newZipEntry(path)\n\t\t\tcompositeEntry = append(compositeEntry, jarEntry)\n\t\t}\n\t\treturn nil\n\t}\n\n\tfilepath.Walk(baseDir, walkFn)\n\n\treturn compositeEntry\n}\n```\n\n2. ZipEntry 路径中包含压缩包\n\n``` go\ntype ZipEntry struct {\n\tabsPath string\n\tzipRC *zip.ReadCloser\n}\n\nfunc newZipEntry(path string) *ZipEntry {\n\tabsPath, err := filepath.Abs(path)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn \u0026ZipEntry{absPath: absPath, zipRC: nil}\n}\n\nfunc (self *ZipEntry) readClass(className string) ([]byte, Entry, error) {\n\tif self.zipRC == nil {\n\t\terr := self.openJar() // 打开jar包\n\t\tif err != nil {\n\t\t\treturn nil, nil, errors.New(\"class not found: \" + className)\n\t\t}\n\t}\n\n\tclassFile := self.findClass(className) // 从已经打开的jar包中寻找对应的class文件\n\tif classFile == nil {\n\t\treturn nil, nil, errors.New(\"class not found: \" + className)\n\t}\n\n\tdata, err := readClass(classFile) // 读取class文件的内容\n\treturn data, self, err\n}\n```\n\n\n3. DirEntry 普通的目录\n\n``` go\ntype DirEntry struct {\n\tabsDir string\n}\n\nfunc newDirEntry(path string) *DirEntry {\n\tabsDir, err := filepath.Abs(path)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn \u0026DirEntry{absDir: absDir}\n}\n\n\nfunc (self *DirEntry) readClass(className string) ([]byte, Entry, error) {\n\tfileName := filepath.Join(self.absDir, className)\n\tdata, err := ioutil.ReadFile(fileName)\n\treturn data, self, err\n}\n```\n\n4. CompositeEntry 路径中包含 `;`对分割出的路径中的每一部分进行构造\n\n``` go\ntype CompositeEntry []Entry\n\nfunc newCompositeEntry(pathList string) CompositeEntry {\n\tCompositeEntry := []Entry{}\n\n\tfor _, path := range strings.Split(pathList, pathListSeparator) {\n\t\tentry := newEntry(path)\n\t\tCompositeEntry = append(CompositeEntry, entry)\n\t}\n\treturn CompositeEntry\n}\n\nfunc (self CompositeEntry) readClass(className string) ([]byte, Entry, error) {\n\tfor _, entry := range self {\n\t\tdata, from, err := entry.readClass(className)\n\t\tif err == nil {\n\t\t\treturn data, from, nil\n\t\t}\n\t}\n\treturn nil, nil, errors.New(\"class not found: \" + className)\n}\n```\n\n- classpath的解析 (现在，我们在构造JVM对象，我们需要先构造一个classpath对象)\n\n**Classpath结构体有三个字段，分别存放三种类路径。Parse()函数使用-Xjre选项解析启动类路径和扩展类路径，使用-classpath/-cp选项解析用户类路径**\n\n``` go\nfunc Parse(jreOption, cpOption string) *Classpath {\n\tcp := \u0026Classpath{}\n\tcp.parseBootAndExtClasspath(jreOption)\n\tcp.parseUserClasspath(cpOption)\n\treturn cp\n}\n```\n\n\n**优先使用用户输入的-Xjre选项作为jre目录。如果没有输入该选项，则在当前目录下寻找jre目录。如果找不到，尝试使用JAVA_HOME环境变量**\n``` go\nfunc (self *Classpath) parseBootAndExtClasspath(jreOption string) {\n\tjreDir := getJreDir(jreOption)\n\n\t// jre/lib*\n\tjreLibPath := filepath.Join(jreDir, \"lib\", \"*\")\n\tself.bootClasspath = newWildcardEntry(jreLibPath)\n\t// jre/lib/ext/*\n\tjreExtPath := filepath.Join(jreDir, \"lib\", \"ext\",\"*\")\n\tself.extClasspath = newWildcardEntry(jreExtPath)\n}\n\nfunc getJreDir(jreOption string) string {\n\tif jreOption != \"\" \u0026\u0026 exists(jreOption) {\n\t\treturn jreOption\n\t}\n\tif exists(\"./jre\") {\n\t\treturn \"./jre\"\n\t}\n\tif jh := os.Getenv(\"JAVA_HOME\"); jh != \"\" {\n\t\treturn filepath.Join(jh, \"jre\")\n\t}\n\tpanic(\"Can not find jre folder\")\n}\n```\n\n**解析用户classPath**\n``` go\nfunc (self *Classpath) parseUserClasspath(cpOption string) {\n\tif cpOption == \"\" {\n\t\tcpOption = \".\"\n\t}\n\tself.userClasspath = newEntry(cpOption)\n}\n```\n\n\n**如果用户没有提供-classpath/-cp选项，则使用当前目录作为用户类路径。ReadClass()方法依次从启动类路径、扩展类路径和用户类路径中搜索class文件**\n\n``` go\nfunc (self *Classpath) ReadClass(className string) ([]byte, Entry, error) {\n\tclassName = className + \".class\"\n\tif data, entry, err := self.bootClasspath.readClass(className); err == nil {\n\t\treturn data, entry, nil\n\t}\n\n\tif data, entry, err := self.extClasspath.readClass(className); err == nil {\n\t\treturn data, entry, nil\n\t}\n\treturn self.userClasspath.readClass(className)\n}\n```\n\n#### classLoader 类加载器对象 (现在我们在构造JVM对象，构造好classpath对象后，我们使用它来构造类加载器对象,注意：这个函数设计的内容非常多)\n\n``` go\n// 类加载器\ntype ClassLoader struct {\n\tcp       *classpath.Classpath\n\tverboseFlag bool\n\tclassMap map[string]*Class\n}\n```\n\nClasspath上面我们刚讲过，其实可以理解为classLoader的数据源，verboseFlag是一个调试用的标志，可以不细究，classMap是类加载器中的数据缓存一样的存在。\n\n``` go\n// 构造函数\nfunc NewClassLoader(cp *classpath.Classpath, verboseFlag bool) *ClassLoader {\n\tloader := \u0026ClassLoader{\n\t\tcp:       cp,\n\t\tverboseFlag: verboseFlag,\n\t\tclassMap: make(map[string]*Class),\n\t}\n\n\tloader.loadBasicClasses()\n\tloader.loadPrimitiveClasses()\n\treturn loader\n}\n```\n\nclassLoader的构造函数调用了一下两个函数，分别加载了`java/lang/Class`和`int` `long` `double` `byte` `char`... 等基本数据类型\n\n``` go\nfunc (self *ClassLoader) loadBasicClasses() {\n\tjlClassClass := self.LoadClass(\"java/lang/Class\")\n\tfor _, class := range self.classMap {\n\t\tif class.jClass == nil {\n\t\t\tclass.jClass = jlClassClass.NewObject()\n\t\t\tclass.jClass.extra = class\n\t\t}\n\t}\n}\n\n// 加载基本数据类型\nfunc (self *ClassLoader) loadPrimitiveClasses() {\n\tfor primitiveType, _ := range primitiveTypes {\n\t\tself.loadPrimitiveClass(primitiveType)\n\t}\n}\n\nfunc (self *ClassLoader) loadPrimitiveClass(className string) {\n\tclass := \u0026Class{\n\t\taccessFlags: ACC_PUBLIC,\n\t\tname: className,\n\t\tloader: self,\n\t\tinitStarted: true,\n\t}\n\tclass.jClass = self.classMap[\"java/lang/Class\"].NewObject()\n\tclass.jClass.extra = class\n\tself.classMap[className] = class\n}\n```\n\n`jlClassClass := self.LoadClass(\"java/lang/Class\")` 中LoadClass()的实现如下\n\n``` go\nfunc (self *ClassLoader) LoadClass(name string) (class *Class) {\n\tif class, ok := self.classMap[name]; ok { // 检测缓存中有没有\n\t\t// alreay loaded\n\t\treturn class\n\t}\n\n\tif name[0] == '[' { // 判断是否是数组类\n\t\tclass = self.loadArrayClass(name)\n\t} else {\n\t\tclass = self.loadNonArrayClass(name)\n\t}\n\n\t// 任意一个class加载时都会关联java.lang.Class的一个实例\n\t// 使之jClass为java.lang.Class的一个实例\n\t// 使之jClass.extra 为其自身\n\tif jlClassClass, ok := self.classMap[\"java/lang/Class\"]; ok {\n\t\tclass.jClass = jlClassClass.NewObject()\n\t\tclass.jClass.extra = class\n\t}\n\treturn\n}\n```\n\n- **对于非数组类，类的加载分为一下三步(构造了类加载器对象后，需要用它去加载一些类，没错，这是包含在classloader的构造函数中的)**\n1. 首先找到 class 文件然后把数据读取到内存中\n2. 解析 class 文件 生成虚拟机可以使用的类数据 并放入方法区\n3. 进行链接\n\n``` go\nfunc (self *ClassLoader) loadNonArrayClass(name string) *Class {\n\tdata, entry := self.readClass(name) // 读取类信息\n\tclass := self.defineClass(data) // 解析类信息\n\tlink(class)\n\tif self.verboseFlag {\n\t\tfmt.Printf(\"[Loaded %s from %s]\\n\", name, entry)\n\t}\n\treturn class\n}\n```\n\n1. 首先找到 class 文件然后把数据读取到内存中\n\n``` go\nfunc (self *ClassLoader) readClass(name string) ([]byte, classpath.Entry) {\n\tdata, entry, err := self.cp.ReadClass(name)\n\tif err != nil {\n\t\tpanic(\"java.lang.ClassNotFoundException: \" + name)\n\t}\n\treturn data, entry\n}\n```\n\n\n2. 解析 class 文件 生成虚拟机可以使用的类数据 并放入方法区\n\n解释一下，方法区，它是运行时数据区的一块逻辑区域，由多个线程共享。方法区主要存放从class文件获取的类信息。此外，类变量也存放在方法区中。当Java虚拟机第一次使用某个类时，它会搜索类路径，找到相应的class文件，然后读取并解析class文件，把相关信息放进方法区。至于方法区到底位于何处，是固定大小还是动态调整，是否参与垃圾回收，以及如何在方法区内存放类数据等，Java虚拟机规范并没有明确规定。\n\n``` go\nfunc (self *ClassLoader) defineClass(data []byte) *Class {\n\tclass := parseClass(data)\n\tclass.loader = self // 绑定加载器\n\tresolveSuperClass(class)\n\tresolveInterfaces(class)\n\tself.classMap[class.name] = class // 注册登记\n\treturn class\n}\n```\n\n``` go\nfunc parseClass(data []byte) *Class {\n\tcf, err := classfile.Parse(data)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn newClass(cf)\n}\n```\n\n#### 简单来看一下class文件的解析(现在我们在构造JVM, 在构造类加载器的过程中，需要去加载class文件)\n\n``` go\ntype ClassFile struct {\n\tmagic uint32\n\tminorVersion uint16\n\tmajorVersion uint16\n\tconstantPool ConstantPool // 常量池\n\taccessFlags uint16\n\tthisClass uint16\n\tsuperClass uint16\n\tinterClass uint16\n\tinterfaces []uint16\n\tfields []*MemberInfo\n\tmethods []*MemberInfo\n\tattributes []AttributeInfo\n}\n```\n\n解析函数的实现很简单：\n\n``` go\nfunc Parse(classData []byte) (cf *ClassFile, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tvar ok bool\n\t\t\terr, ok = r.(error)\n\t\t\tif !ok {\n\t\t\t\terr = fmt.Errorf(\"%v\", r)\n\t\t\t}\n\t\t}\n\t} ()\n\n\tcr := \u0026ClassReader{classData}\n\tcf = \u0026ClassFile{}\n\tcf.read(cr)\n\treturn\n}\n```\n\n解析后对应字段赋值\n\n``` go\nfunc (self *ClassFile) read(reader *ClassReader) {\n\tself.readAndCheckMagic(reader)\n\tself.readAndCheckVersion(reader)\n\tself.constantPool = readConstantPool(reader)\n\tself.accessFlags = reader.readUint16()\n\tself.thisClass = reader.readUint16()\n\tself.superClass = reader.readUint16()\n\tself.interfaces = reader.readUint16s()\n\tself.fields = readMembers(reader, self.constantPool)\n\tself.methods = readMembers(reader, self.constantPool)\n\tself.attributes = readAttributes(reader, self.constantPool)\n}\n```\n\n需要注意的是 `readConstantPool` 、`readMembers` 以及 `readAttributes` 的实现\n\n- 常量池 常量池类似于SymbolTable (现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件，现在来解析其中的常量池)\n\n**常量池占据了class文件很大一部分数据，里面存放着各式各样的常量信息，包括数字和字符串常量、类和接口名、字段和方法名，等等**\n\n**可以把常量池中的常量分为两类：字面量（literal）和符号引用（symbolic reference）。字面量包括数字常量和字符串常量，符号引用包括类和接口名、字段和方法信息等。除了字面量，其他常量都是通过索引直接或间接指向CONSTANT_Utf8_info常量**\n\n``` java\npublic class HelloWorld {\n    // 静态常量\n    public static final double PI = 3.14;\n    // 声明成员常量\n    final int y = 10;\n    public static void main(String[] args) {\n        // 声明局部常量\n        final double x = 3.3;\n    }\n}\n```\n\n常量池的实现是通过`type ConstantPool []ConstantInfo` 构造一个ConstantInfo的Slice\n\n``` go\ntype ConstantInfo interface {\n\treadInfo(reader *ClassReader)\n}\n```\n\ncp的构造方法：\n``` go\nfunc readConstantPool(reader *ClassReader) ConstantPool {\n\tcpCount := int(reader.readUint16())\n\tcp := make([]ConstantInfo, cpCount)\n\n\t// 遍历类文件中的常量数据 逐个读取并保存在数组中\n\tfor i := 1;i \u003c cpCount;i++ {\n\t\tcp[i] = readConstantInfo(reader, cp)\n\n\t\tswitch cp[i].(type) {\n\t\tcase *ConstantLongInfo, *ConstantDoubleInfo: // Long 和 Double 占用两个位置\n\t\t\ti++\n\t\t}\n\t}\n\treturn cp\n}\n```\n\n``` go\nfunc readConstantInfo(reader *ClassReader, cp ConstantPool) ConstantInfo {\n\ttag := reader.readUint8()\n\tc := newConstantInfo(tag, cp) // 根据不同数据类型的tag 构造不同的常量信息\n\tc.readInfo(reader)\n\treturn c\n}\n```\n\n经过这样readConstantPool中的循环，ConstantPool中就保存了class文件中的常量信息。通过Index可以获取对应的常量信息\n\n``` go\nfunc (self ConstantPool) getConstantInfo(index uint16) ConstantInfo {\n\tif cpInfo := self[index]; cpInfo != nil {\n\t\treturn cpInfo\n\t}\n\tpanic(fmt.Errorf(\"Invalid constant pool index: %v!\", index))\n}\n```\n\n\n- 字段与方法(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件，现在来解析其中的字段和方法)\n\n``` go\nfunc readMembers(reader *ClassReader, cp ConstantPool) []*MemberInfo  {\n\tmemberCount := reader.readUint16()\n\tmembers := make([]*MemberInfo, memberCount)\n\tfor i := range members {\n\t\tmembers[i] = readMember(reader, cp)\n\t}\n\treturn members\n}\n\nfunc readMember(reader *ClassReader, cp ConstantPool) *MemberInfo {\n\treturn \u0026MemberInfo{\n\t\tcp: cp,\n\t\taccessFlags: reader.readUint16(),\n\t\tnameIndex: reader.readUint16(),\n\t\tdescriptorIndex: reader.readUint16(),\n\t\tattributes: readAttributes(reader, cp),\n\t}\n}\n```\n\n- 属性表 同样是一个AttributeInfo的Slice(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件，现在来解析其中的属性表)\n\n\n``` go\ntype AttributeInfo interface {\n\treadInfo(reader *ClassReader)\n}\n\nfunc readAttributes(reader *ClassReader, cp ConstantPool) []AttributeInfo {\n\tattributesCount := reader.readUint16()\n\tattributes := make([]AttributeInfo, attributesCount)\n\tfor i := range attributes {\n\t\tattributes[i] = readAttribute(reader, cp)\n\t}\n\treturn attributes\n}\n\nfunc readAttribute(reader *ClassReader, cp ConstantPool) AttributeInfo {\n\tattrNameIndex := reader.readUint16()\n\tattrName := cp.getUtf8(attrNameIndex)\n\tattrLen := reader.readUint32()\n\tattrInfo := newAttributeInfo(attrName, attrLen, cp) // 根据不同的attrName 构造不同的attrInfo\n\tattrInfo.readInfo(reader)\n\treturn attrInfo\n}\n```\n\n\n``` go\nfunc newAttributeInfo(attrName string, attrLen uint32,\n\tcp ConstantPool) AttributeInfo {\n\tswitch attrName {\n\t// Code是变长属性，只存在于method_info结构中。Code属性中存放字节码等方法相关信息\n\tcase \"Code\":\n\t  return \u0026CodeAttribute{cp: cp}\n\t// ConstantValue是定长属性，只会出现在field_info结构中，用于表示常量表达式的值\n\tcase \"ConstantValue\":\n      return \u0026ConstantValueAttribute{}\n\t//  Deprecated，仅起标记作用，不包含任何数据\n    case \"Deprecated\":\n      return \u0026DeprecatedAttribute{}\n    // Exceptions是变长属性，记录方法抛出的异常表\n    case \"Exceptions\":\n      return \u0026ExceptionsAttribute{}\n    // LineNumberTable属性表存放方法的行号信息\n    case \"LineNumberTable\":\n      return \u0026LineNumberTableAttribute{}\n    // LocalVariableTable属性表中存放方法的局部变量信息\n    case \"LocalVariableTable\":\n      return \u0026LocalVariableTableAttribute{}\n    // SourceFile是可选定长属性，只会出现在ClassFile结构中，用于指出源文件名\n    case \"SourceFile\":\n      return \u0026SourceFileAttribute{cp: cp}\n    //  Synthetic，仅起标记作用，不包含任何数据\n    case \"Synthetic\":\n      return \u0026SyntheticAttribute{}\n    default:\n    return \u0026UnparsedAttribute{attrName, attrLen, nil}\n\t}\n}\n```\n\n解析过class文件后,调用newClass() 来构造该类(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件,现在使用这些类信息来构造class对象)\n\n``` go\ntype Class struct {\n\taccessFlags       uint16 // 访问级别\n\tname              string // thisClassName\n\tsuperClassName    string // 父类名\n\tinterfaceNames    []string // 实现的接口名\n\tconstantPool      *ConstantPool // 运行时常量池指针\n\tfields            []*Field // 类字段\n\tmethods           []*Method // 类方法\n\tsourceFile        string // 源文件\n\tloader            *ClassLoader // 类加载器\n\tsuperClass        *Class // 父类指针\n\tinterfaces        []*Class // 实现的接口表\n\tinstanceSlotCount uint // 运行时数据占用的槽量\n\tstaticSlotCount   uint // 静态数据占用的槽量\n\tstaticVars        Slots // 静态数据\n\tinitStarted       bool // 表示类的\u003cclinit\u003e方法是否已经开始执行\n\tjClass            *Object // java.lang.Class的变量引用\n}\n\nfunc newClass(cf *classfile.ClassFile) *Class {\n\tclass := \u0026Class{}\n\tclass.accessFlags = cf.AccessFlags()\n\tclass.name = cf.ClassName()\n\tclass.superClassName = cf.SuperClassName()\n\tclass.interfaceNames = cf.InterfaceNames()\n\tclass.constantPool = newConstantPool(class, cf.ConstantPool()) // 加载运行时常量池\n\tclass.fields = newFileds(class, cf.Fields())                   // 加载运行时字段\n\tclass.methods = newMethods(class, cf.Methods())                // 加载运行时方法\n\tclass.sourceFile = getSourceFile(cf)\n\treturn class\n}\n```\n\n对于这段代码，需要注意的有几个：newConstantPool() newFields() newMethods()\n\n- `newConstantPool(class, cf.ConstantPool())` 类对象的运行时常量池指针\n\n``` go\nfunc newConstantPool(class *Class, cfCp classfile.ConstantPool) *ConstantPool {\n\tcpCount := len(cfCp)\n\tconsts := make([]Constant, cpCount)\n\trtCp := \u0026ConstantPool{class, consts}\n\n\tfor i := 1;i \u003c cpCount;i++ {\n\t\tcpInfo := cfCp[i]\n\t\tswitch cpInfo.(type) {\n\t\tcase *classfile.ConstantIntegerInfo:\n\t\t\tintInfo := cpInfo.(*classfile.ConstantIntegerInfo)\n\t\t\tconsts[i] = intInfo.Value()\n        // ...\n\t\tcase *classfile.ConstantStringInfo:\n\t\t\tstringInfo := cpInfo.(*classfile.ConstantStringInfo)\n\t\t\tconsts[i] = stringInfo.String()\n\t\tcase *classfile.ConstantClassInfo:\n\t\t\tclassInfo := cpInfo.(*classfile.ConstantClassInfo)\n\t\t\tconsts[i] = newClassRef(rtCp, classInfo) // 当前类的一个引用\n\t\tcase *classfile.ConstantFieldrefInfo:\n\t\t\tfieldrefInfo := cpInfo.(*classfile.ConstantFieldrefInfo)\n\t\t\tconsts[i] = newFieldRef(rtCp, fieldrefInfo)\n\t\tcase *classfile.ConstantMethodrefInfo:\n\t\t\tmethodrefInfo := cpInfo.(*classfile.ConstantMethodrefInfo)\n\t\t\tconsts[i] = newMethodRef(rtCp, methodrefInfo)\n\t\tcase *classfile.ConstantInterfaceMethodrefInfo:\n\t\t\tmethodrefInfo := cpInfo.(*classfile.ConstantInterfaceMethodrefInfo)\n\t\t\tconsts[i] = newInterfaceMethodRef(rtCp, methodrefInfo)\n\t\tdefault:\n\t\t\t// TODO\n\t\t}\n\t}\n\treturn rtCp\n}\n```\n不难看出，其实这个函数就是解析了classfile中的ConstantPool对象，将其中保存的数据进行映射。值得注意的是引用类型的数据:\n\n``` go\n// 根据class文件中存储的类常量创建ClassRef实例\nfunc newClassRef(cp *ConstantPool, classInfo *classfile.ConstantClassInfo) *ClassRef {\n\tref := \u0026ClassRef{}\n\tref.cp = cp\n\tref.className = classInfo.Name()\n\treturn ref\n}\n```\n\n``` go\nfunc newFieldRef(cp *ConstantPool, refInfo *classfile.ConstantFieldrefInfo) *FieldRef{\n\tref := \u0026FieldRef{}\n\tref.cp = cp\n\tref.copyMemberRefInfo(\u0026refInfo.ConstantMemberrefInfo)\n\treturn ref\n}\n```\n\nMethod 与 Interface_methodRef的实现与field_ref的基本一致，都是拷贝一些必要的信息给引用\n\n``` go\nfunc (self *MemberRef) copyMemberRefInfo(refInfo *classfile.ConstantMemberrefInfo) {\n\tself.className = refInfo.ClassName()\n\tself.name, self.descriptor = refInfo.NameAndDescriptor()\n}\n```\n\n- newFields 类对象的运行时字段\n\n``` go\ntype ClassMember struct {\n\taccessFlags uint16 // 访问级别\n\tname string // 类名\n\tdescriptor string // 描述符\n\tclass *Class // 类指针\n}\n```\n\n``` go\ntype Field struct {\n\tClassMember\n\tconstValueIndex uint\n\tslotId uint\n}\n\n//  classfile.MemberInfo 转换为 Fileds\nfunc newFileds(class *Class, cfFields []*classfile.MemberInfo) []*Field {\n\tfields := make([]*Field, len(cfFields))\n\tfor i, cfField := range cfFields {\n\t\tfields[i] = \u0026Field{}\n\t\tfields[i].class = class\n\t\tfields[i].copyMemberInfo(cfField)\n\t\tfields[i].copyAttributes(cfField)\n\t}\n\treturn fields\n}\n```\n\n- newMethods 类对象的运行时方法\n\n``` go\ntype Method struct {\n\tClassMember\n\tmaxStack uint\n\tmaxLocals uint\n\tcode []byte\n\texceptionTable ExceptionTable\n\tlineNumberTable *classfile.LineNumberTableAttribute\n\targSlotCount uint\n}\n\nfunc newMethods(class *Class, cfMethods []*classfile.MemberInfo) []*Method {\n\tmethods := make([]*Method, len(cfMethods))\n\tfor i, cfMethod := range cfMethods {\n\t\tmethods[i] = newMethod(class, cfMethod)\n\t}\n\treturn methods\n}\n```\n\n**构造好类对象后，我们要把这个类注册到类加载器的classMap中,提醒一下，我们现在还没有走出classLoader的构造函数**\n\n``` go\nfunc (self *ClassLoader) loadNonArrayClass(name string) *Class {\n\tdata, entry := self.readClass(name) // 读取类信息\n\tclass := self.defineClass(data) // 解析类信息\n\tlink(class)\n\tif self.verboseFlag {\n\t\tfmt.Printf(\"[Loaded %s from %s]\\n\", name, entry)\n\t}\n\treturn class\n}\n```\n\n``` go\nfunc (self *ClassLoader) defineClass(data []byte) *Class {\n\tclass := parseClass(data)\n\tclass.loader = self // 绑定加载器\n\tresolveSuperClass(class)\n\tresolveInterfaces(class)\n\tself.classMap[class.name] = class // 注册登记\n\treturn class\n}\n```\n\n**以上函数是类解析过程的第二步, 看一下resolveSuperClass(class)和resolveInterfaces(class)的实现**\n\n``` go\nfunc resolveSuperClass(class *Class) {\n\tif class.name != \"java/lang/Object\" { // Object没有父类\n        // 递归向上加载类\n\t\tclass.superClass = class.loader.LoadClass(class.superClassName)\n\t}\n}\n\nfunc resolveInterfaces(class *Class) {\n\tinterfaceCount := len(class.interfaceNames)\n\tif interfaceCount \u003e 0 {\n\t\tclass.interfaces = make([]*Class, interfaceCount)\n\t\tfor i, interfaceName := range class.interfaceNames {\n            // 逐个加载接口类\n\t\t\tclass.interfaces[i] = class.loader.LoadClass(interfaceName)\n\t\t}\n\t}\n}\n```\n\n\n3. 进行链接\n\n``` go\nfunc link(class *Class) {\n\tprepare(class)\n}\n\nfunc prepare(class *Class) {\n\tcalcInstanceFieldSlotIds(class)\n\tcalcStaticFieldSlotIds(class)\n\tallocAndInitStaticVars(class)\n}\n\n```\n\n计算类实例数据以及静态数据占用的槽量\n``` go\n// calculate how many Instantce vars do we need\nfunc calcInstanceFieldSlotIds(class *Class) {\n\tslotId := uint(0)\n\tif class.superClass != nil {\n\t\tslotId = class.superClass.instanceSlotCount\n\t}\n\tfor _, field := range class.fields {\n\t\tif !field.IsStatic() {\n\t\t\tfield.slotId = slotId\n\t\t\tslotId++\n\t\t\tif field.isLongOrDouble() {\n\t\t\t\tslotId++\n\t\t\t}\n\t\t}\n\t}\n\tclass.instanceSlotCount = slotId // how many slot we need\n}\n\n// calculate how many STATIC vars do we need\nfunc calcStaticFieldSlotIds(class *Class) {\n\tslotId := uint(0)\n\tfor _, field := range class.fields {\n\t\tif field.IsStatic() {\n\t\t\tfield.slotId = slotId\n\t\t\tslotId++\n\t\t\tif field.isLongOrDouble() {\n\t\t\t\tslotId++\n\t\t\t}\n\t\t}\n\t}\n\tclass.staticSlotCount = slotId // how many slot we need\n}\n```\n\n\n分配内存并初始化静态变量\n``` go\nfunc allocAndInitStaticVars(class *Class) {\n\tclass.staticVars = newSlots(class.staticSlotCount) // allocate mem for static vars\n\tfor _, field := range class.fields {\n\t\tif field.IsStatic() \u0026\u0026 field.IsFinal() {\n\t\t\tinitStaticFinalVar(class, field) // init the satic final vars\n\t\t}\n\t}\n}\n\nfunc initStaticFinalVar(class *Class, field *Field) {\n\tvars := class.staticVars\n\tcp := class.constantPool\n\tcpIndex := field.ConstValueIndex()\n\tslotId := field.SlotId()\n\n\tif cpIndex \u003e 0 {\n\t\tswitch field.Descriptor() {\n\t\tcase \"Z\", \"B\", \"C\", \"S\", \"I\":\n\t\t\tval := cp.GetConstant(cpIndex).(int32)\n\t\t\tvars.SetInt(slotId, val)\n        // ...\n\t\t}\n\t}\n}\n```\n\n\n- **对于数组类** (我们正在构造JVM对象，构造了classpath后，正在构造classloader，构造classloader时需要去预加载一些class，在这个过程中分为非数组类和数组类)\n\n``` go\nfunc (self *ClassLoader) LoadClass(name string) (class *Class) {\n\n\tif name[0] == '[' { // 判断是否是数组类\n\t\tclass = self.loadArrayClass(name)\n\t} else {\n\t\tclass = self.loadNonArrayClass(name)\n\t}\n\n}\n```\n\n\n``` go\nfunc (self *ClassLoader) loadArrayClass(name string) *Class {\n\t// int[]{1, 2, 3, 4} 这就是一个数组类 [I\n\tclass := \u0026Class{\n\t\taccessFlags: ACC_PUBLIC,\n\t\tname: name,\n\t\tloader: self,\n\t\tinitStarted: true, // 数组类不需要初始化\n\t\tsuperClass: self.LoadClass(\"java/lang/Object\"),\n\t\tinterfaces: []*Class{ // 实现了 以下两个类\n\t\t\tself.LoadClass(\"java/lang/Cloneable\"),\n\t\t\tself.LoadClass(\"java/io/Serializable\"),\n\t\t},\n\t}\n\tself.classMap[name] = class\n\treturn class\n}\n```\n\n\n#### 类的实例化\n\n**为加载好的对象创建一个java/lang/Class的实例作为类信息**\n\n``` go\nfunc (self *ClassLoader) loadBasicClasses() {\n\tjlClassClass := self.LoadClass(\"java/lang/Class\")\n\tfor _, class := range self.classMap {\n\t\tif class.jClass == nil {\n\t\t\tclass.jClass = jlClassClass.NewObject()\n\t\t\tclass.jClass.extra = class\n\t\t}\n\t}\n}\n```\n\n``` go\nfunc (self *Class) NewObject() *Object {\n\treturn newObject(self)\n}\n```\n\n``` go\ntype Object struct {\n\tclass *Class\n\tdata interface{}\n\textra interface{} // 记录Object结构体实例的额外信息\n}\n\nfunc newObject(class *Class) *Object {\n\treturn \u0026Object{\n\t\tclass: class,\n\t\tdata: newSlots(class.instanceSlotCount), // 分配类实例内存\n\t}\n}\n```\n\nSlot是实际存储数据的槽\n\n``` go\ntype Slot struct {\n\tnum int32 // 数值\n\tref *Object // 引用\n}\n\ntype Slots []Slot\n\nfunc newSlots(slotCount uint) Slots {\n\tif slotCount \u003e 0 {\n\t\treturn make([]Slot, slotCount)\n\t}\n\treturn nil\n}\n```\n\n#### 类加载器构造结束\n\n经过漫长的类加载过程，我们终于构造好了类加载器，我们做了什么呢？\n\n1. 我们构造了classpath对象，获取了class文件的数据源\n2. 构造了classloader对象，但它里面暂时还没有数据\n3. 使用它去加载了java/lang/Class以及一些基本数据类型\n4. 类加载器会去从classpath中找到这些类的class文件，读取、解析、链接\n5. 预加载过这些类后，为每一个类的jlClass创建一个java/lang/Class实例作为类信息对外暴露\n\n#### jvm的线程\n\n上面，我们构造了类加载器，作为JVM的最后一部分，我们来看一下mainThread主线程\n\n``` go\nfunc newJVM(cmd *Cmd) *JVM {\n\tcp := classpath.Parse(cmd.XjreOption, cmd.cpOption)\n\tclassLoader := heap.NewClassLoader(cp, cmd.verboseClassFlag)\n\treturn \u0026JVM{\n\t\tcmd: cmd,\n\t\tclassLoader: classLoader,\n\t\tmainThread: rtda.NewThread(),\n\t}\n}\n```\n\n由于我们只是实现一个简单的玩具，并不考虑实现多线程。\n\n``` go\n/*\nJVM\n   Thread\n      pc\n      Stack\n        Frame\n           LocalVars\n           OperandStack\n  **/\n\n\ntype Thread struct {\n\tpc int\n\tstack *Stack\n}\n\nfunc NewThread() *Thread {\n\treturn \u0026Thread{\n\t\tstack: newStack(1024), // 最多存放1024个栈帧\n\t}\n}\n```\n\njvm中一个抽象的栈\n\n``` go\ntype Stack struct {\n\tmaxSize uint\n\tsize uint\n\t_top *Frame\n}\n\nfunc newStack(maxSize uint) *Stack {\n\treturn \u0026Stack{\n\t\tmaxSize: maxSize,\n\t}\n}\n```\n\n### 初始化\n\n``` go\nfunc (self *JVM) start() {\n\tself.initVM()\n\tself.execMain()\n}\n\nfunc (self *JVM) initVM() {\n\tvmClass := self.classLoader.LoadClass(\"sun/misc/VM\")\n\tbase.InitClass(self.mainThread, vmClass)\n\tinterpret(self.mainThread, self.cmd.verboseInstFlag)\n}\n```\n\n**类的加载已经讲过了，现在去初始化类**\n\n``` go\n// 初始化类\nfunc InitClass(thread *rtda.Thread, class *heap.Class) {\n    // 标志已经初始化\n\tclass.StartInit()\n    // 构造函数压栈\n\tscheduleClinit(thread, class)\n    // 递归构造父类\n\tinitSuperClass(thread, class)\n}\n\nfunc scheduleClinit(thread *rtda.Thread, class *heap.Class) {\n\tclinit := class.GetClinitMethod()\n\tif clinit != nil {\n\t\t// exec \u003cclinit\u003e\n\t\tnewFrame := thread.NewFrame(clinit)\n\t\tthread.PushFrame(newFrame)\n\t}\n}\n\nfunc initSuperClass(thread *rtda.Thread, class *heap.Class) {\n\tif !class.IsInterface() { // not a interface\n\t\tsuperClass := class.SuperClass()\n\t\tif superClass != nil \u0026\u0026 !superClass.InitStarted() {\n\t\t\tInitClass(thread, superClass)\n\t\t}\n\t}\n}\n\n```\n\n来说一下 GetClinitMethod 和 Frame的几个操作\n\n``` go\nfunc (self *Class) GetClinitMethod() *Method {\n\treturn self.getMethod(\"\u003cclinit\u003e\", \"()V\", true)\n}\n\nfunc (self *Class) getMethod(name, descriptor string, isStatic bool) *Method {\n\tfor c := self; c != nil; c = c.superClass {\n\t\tfor _, method := range c.methods {\n\t\t\tif method.IsStatic() == isStatic \u0026\u0026\n\t\t\t\tmethod.name == name \u0026\u0026 method.descriptor == descriptor {\n\t\t\t\treturn method\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n```\n\n从当前类及其父类中寻找名为\"\u003cclinit\u003e\"描述符为\"()V\"的方法\n\n- Frame的操作\n\n``` go\nfunc (self *Thread) NewFrame(method *heap.Method) *Frame {\n\treturn newFrame(self, method)\n}\n\ntype Frame struct {\n\tlower *Frame\n\tlocalVars LocalVars\n\toperandStack *OperandStack\n\tthread *Thread\n\tmethod *heap.Method\n\tnextPC int // the next instruction after the call\n}\n\nfunc newFrame(thread *Thread, method *heap.Method) *Frame {\n\treturn \u0026Frame {\n\t\tthread: thread,\n\t\tmethod: method,\n\t\tlocalVars: newLocalVars(method.MaxLocals()),\n\t\toperandStack: newOperandStack(method.MaxStack()),\n\t}\n}\n```\n\n这里的Frame是一个链表的节点，我们使用链表来模拟一个Stack. 每个Frame就是一个函数栈帧，函数栈帧中又包含了局部变量和操作数栈。\n\nPushFrame相当于将当前函数栈帧压入线程栈顶，这样执行时就会操作该栈内的数据\n\n`interpret`函数在下一节进行解说\n\n### 执行字节码\n\n在class文件文件中会有Code AttributeInfo 其中包含了jvm字节码，通过解析这些字节码，翻译成对应的指令，再逐步执行这些我们已经实现好了的指令，就实现了一个有效的虚拟机\n\n``` go\nfunc (self *JVM) start() {\n\tself.initVM()\n\tself.execMain()\n}\n\nfunc (self *JVM) execMain() {\n\tclassName := strings.Replace(self.cmd.class, \".\", \"/\", -1)\n\tmainClass := self.classLoader.LoadClass(className)\n\tmainMethod := mainClass.GetMainMethod()\n\tif mainMethod == nil {\n\t\tfmt.Printf(\"Main method not found in class %s\\n\", self.cmd.class)\n\t\treturn\n\t}\n\n\targsArr := self.createArgsArray()\n\tframe := self.mainThread.NewFrame(mainMethod)\n\tframe.LocalVars().SetRef(0, argsArr)\n\tself.mainThread.PushFrame(frame)\n\tinterpret(self.mainThread, self.cmd.verboseInstFlag)\n}\n```\n\n执行Main方法时，先加载当前主类，然后找到main方法，为main方法新建一个函数栈帧，设置函数本地变量表的第0位为cli传入的参数\n\n``` go\nfunc interpret(thread *rtda.Thread, logInst bool) {\n\tdefer catchErr(thread)\n\tloop(thread, logInst)\n}\n\nfunc catchErr(thread *rtda.Thread) {\n\tif r := recover(); r != nil {\n\t\tlogFrames(thread)\n\t\tpanic(r)\n\t}\n}\n\nfunc loop(thread *rtda.Thread, logInst bool) {\n\treader := \u0026base.BytecodeReader{}\n\tfor {\n\t\tframe := thread.CurrentFrame() // 当前函数栈帧\n\t\tpc := frame.NextPC()\n\t\tthread.SetPC(pc)\n\n\t\t// decode\n\t\treader.Reset(frame.Method().Code(), pc)\n\t\topcode := reader.ReadUint8()\n\t\tinst := instructions.NewInstruction(opcode)\n\t\tinst.FetchOperands(reader)\n\t\tframe.SetNextPC(reader.PC())\n\n\t\tif logInst {\n\t\t\tlogInstruction(frame, inst)\n\t\t}\n\n\t\t// execute\n\t\tinst.Execute(frame)\n\t\tif thread.IsStackEmpty() {\n\t\t\tbreak\n\t\t}\n\t}\n}\n```\n\n\n当loop循环开始后，位于mainThread栈顶的是mainFrame，向后移动PC后，构造Instruction对象，开始解码当前函数的的字节码，然后执行该指令的Execute()\n\n``` go\n\nfunc NewInstruction(opcode byte) base.Instruction {\n\tswitch opcode {\n\tcase 0x00:\n\t\treturn nop\n    // ... 很多指令 200条左右\n\tcase 0xbb:\n\t\treturn \u0026NEW{}\n\tcase 0xbc:\n\t\treturn \u0026NEW_ARRAY{}\n\tcase 0xbd:\n\t\treturn \u0026ANEW_ARRAY{}\n\tcase 0xbe:\n\t\treturn arraylength\n\tcase 0xbf:\n\t\treturn athrow\n\tcase 0xc0:\n\t\treturn \u0026CHECK_CAST{}\n\tcase 0xc1:\n\t\treturn \u0026INSTANCE_OF{}\n\tcase 0xc4:\n\t\treturn \u0026WIDE{}\n\tcase 0xc5:\n\t\treturn \u0026MULTI_ANEW_ARRAY{}\n\tcase 0xc6:\n\t\treturn \u0026IFNULL{}\n\tcase 0xc7:\n\t\treturn \u0026IFNONNULL{}\n\tcase 0xc8:\n\t\treturn \u0026GOTO_W{}\n\tcase 0xfe:\n\t\treturn invoke_native\n\tdefault:\n\t\tpanic(fmt.Errorf(\"Unsupported opcode: 0x%x!\", opcode))\n\t}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimpact-eintr%2Fjvmgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimpact-eintr%2Fjvmgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimpact-eintr%2Fjvmgo/lists"}