接下来,我们按照函数的执行顺序追踪一下JVM的实现,我们需要向计算机一样压很多层栈。当然我会保存一些上下文,免得阅读时读到一半又要跳回来。
代码详见https://github.com/impact-eintr/jvmgo
主函数
func main() {
cmd := parseCmd()
if cmd.versionFlag {
fmt.Println("version: v0.0.1")
} else if cmd.helpFlag || cmd.class == "" {
printUsage()
} else {
newJVM(cmd).start()
}
}
命令行解析
type Cmd struct {
helpFlag bool
versionFlag bool
verboseClassFlag bool
verboseInstFlag bool
cpOption string
XjreOption string
class string
args []string
}
- 解析命令
func parseCmd() *Cmd {
cmd := &Cmd{}
flag.Usage = printUsage
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.BoolVar(&cmd.verboseClassFlag, "verbose:class", false, "enable verbose output")
flag.BoolVar(&cmd.verboseInstFlag, "verbose:inst", false, "enable verbose output")
flag.StringVar(&cmd.cpOption, "classpath", "", "classpath")
flag.StringVar(&cmd.cpOption, "cp", "", "classpath")
flag.StringVar(&cmd.XjreOption, "Xjre", "", "path to jre")
flag.Parse()
args := flag.Args()
if (len(args) > 0) {
cmd.class = args[0]
cmd.args = args[1:]
}
return cmd
}
捕获对应的字符,绑定到Cmd的字段。
构造虚拟机
newJVM(cmd).start()
- 虚拟机对象
type JVM struct {
cmd *Cmd
classLoader *heap.ClassLoader
mainThread *rtda.Thread
}
- cmd cli解析器
- classLoader 类加载器
- mainThread 运行时主线程
- 构造JVM对象
func newJVM(cmd *Cmd) *JVM {
cp := classpath.Parse(cmd.XjreOption, cmd.cpOption)
classLoader := heap.NewClassLoader(cp, cmd.verboseClassFlag)
return &JVM{
cmd: cmd,
classLoader: classLoader,
mainThread: rtda.NewThread(),
}
}
classpath
首先来看classpath对象
type Classpath struct {
bootClasspath Entry
extClasspath Entry
userClasspath Entry
}
Entry
是一个 interface
type Entry interface {
readClass(className string) ([]byte, Entry, error)
String() string
}
readClass
是不同类型classpath对应的读取class对象的实现方法。
String
不同类型classpath对应的路径
func newEntry(path string) Entry {
if strings.Contains(path, pathListSeparator) {
return newCompositeEntry(path)
}
if strings.HasSuffix(path, "*") {
return newWildcardEntry(path)
}
if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") ||
strings.HasSuffix(path, ".zip") || strings.HasSuffix(path, ".ZIP") {
return newZipEntry(path)
}
return newDirEntry(path)
}
可以看到有4种Entry
- WildcardEntry 路径中包含通配符*
func newWildcardEntry(path string) CompositeEntry {
baseDir := path[:len(path)-1]
compositeEntry := []Entry{}
// 对某个路径下的所有文件执行此函数
walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() && path != baseDir {
return filepath.SkipDir
}
// 对jar包构建Entry
if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, ".JAR") {
jarEntry := newZipEntry(path)
compositeEntry = append(compositeEntry, jarEntry)
}
return nil
}
filepath.Walk(baseDir, walkFn)
return compositeEntry
}
- ZipEntry 路径中包含压缩包
type ZipEntry struct {
absPath string
zipRC *zip.ReadCloser
}
func newZipEntry(path string) *ZipEntry {
absPath, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return &ZipEntry{absPath: absPath, zipRC: nil}
}
func (self *ZipEntry) readClass(className string) ([]byte, Entry, error) {
if self.zipRC == nil {
err := self.openJar() // 打开jar包
if err != nil {
return nil, nil, errors.New("class not found: " + className)
}
}
classFile := self.findClass(className) // 从已经打开的jar包中寻找对应的class文件
if classFile == nil {
return nil, nil, errors.New("class not found: " + className)
}
data, err := readClass(classFile) // 读取class文件的内容
return data, self, err
}
- DirEntry 普通的目录
type DirEntry struct {
absDir string
}
func newDirEntry(path string) *DirEntry {
absDir, err := filepath.Abs(path)
if err != nil {
panic(err)
}
return &DirEntry{absDir: absDir}
}
func (self *DirEntry) readClass(className string) ([]byte, Entry, error) {
fileName := filepath.Join(self.absDir, className)
data, err := ioutil.ReadFile(fileName)
return data, self, err
}
- CompositeEntry 路径中包含
;
对分割出的路径中的每一部分进行构造
type CompositeEntry []Entry
func newCompositeEntry(pathList string) CompositeEntry {
CompositeEntry := []Entry{}
for _, path := range strings.Split(pathList, pathListSeparator) {
entry := newEntry(path)
CompositeEntry = append(CompositeEntry, entry)
}
return CompositeEntry
}
func (self CompositeEntry) readClass(className string) ([]byte, Entry, error) {
for _, entry := range self {
data, from, err := entry.readClass(className)
if err == nil {
return data, from, nil
}
}
return nil, nil, errors.New("class not found: " + className)
}
- classpath的解析 (现在,我们在构造JVM对象,我们需要先构造一个classpath对象)
Classpath结构体有三个字段,分别存放三种类路径。Parse()函数使用-Xjre选项解析启动类路径和扩展类路径,使用-classpath/-cp选项解析用户类路径
func Parse(jreOption, cpOption string) *Classpath {
cp := &Classpath{}
cp.parseBootAndExtClasspath(jreOption)
cp.parseUserClasspath(cpOption)
return cp
}
优先使用用户输入的-Xjre选项作为jre目录。如果没有输入该选项,则在当前目录下寻找jre目录。如果找不到,尝试使用JAVA_HOME环境变量
func (self *Classpath) parseBootAndExtClasspath(jreOption string) {
jreDir := getJreDir(jreOption)
// jre/lib*
jreLibPath := filepath.Join(jreDir, "lib", "*")
self.bootClasspath = newWildcardEntry(jreLibPath)
// jre/lib/ext/*
jreExtPath := filepath.Join(jreDir, "lib", "ext","*")
self.extClasspath = newWildcardEntry(jreExtPath)
}
func getJreDir(jreOption string) string {
if jreOption != "" && exists(jreOption) {
return jreOption
}
if exists("./jre") {
return "./jre"
}
if jh := os.Getenv("JAVA_HOME"); jh != "" {
return filepath.Join(jh, "jre")
}
panic("Can not find jre folder")
}
解析用户classPath
func (self *Classpath) parseUserClasspath(cpOption string) {
if cpOption == "" {
cpOption = "."
}
self.userClasspath = newEntry(cpOption)
}
如果用户没有提供-classpath/-cp选项,则使用当前目录作为用户类路径。ReadClass()方法依次从启动类路径、扩展类路径和用户类路径中搜索class文件
func (self *Classpath) ReadClass(className string) ([]byte, Entry, error) {
className = className + ".class"
if data, entry, err := self.bootClasspath.readClass(className); err == nil {
return data, entry, nil
}
if data, entry, err := self.extClasspath.readClass(className); err == nil {
return data, entry, nil
}
return self.userClasspath.readClass(className)
}
classLoader 类加载器对象 (现在我们在构造JVM对象,构造好classpath对象后,我们使用它来构造类加载器对象,注意:这个函数设计的内容非常多)
// 类加载器
type ClassLoader struct {
cp *classpath.Classpath
verboseFlag bool
classMap map[string]*Class
}
Classpath上面我们刚讲过,其实可以理解为classLoader的数据源,verboseFlag是一个调试用的标志,可以不细究,classMap是类加载器中的数据缓存一样的存在。
// 构造函数
func NewClassLoader(cp *classpath.Classpath, verboseFlag bool) *ClassLoader {
loader := &ClassLoader{
cp: cp,
verboseFlag: verboseFlag,
classMap: make(map[string]*Class),
}
loader.loadBasicClasses()
loader.loadPrimitiveClasses()
return loader
}
classLoader的构造函数调用了一下两个函数,分别加载了java/lang/Class
和int
long
double
byte
char
… 等基本数据类型
func (self *ClassLoader) loadBasicClasses() {
jlClassClass := self.LoadClass("java/lang/Class")
for _, class := range self.classMap {
if class.jClass == nil {
class.jClass = jlClassClass.NewObject()
class.jClass.extra = class
}
}
}
// 加载基本数据类型
func (self *ClassLoader) loadPrimitiveClasses() {
for primitiveType, _ := range primitiveTypes {
self.loadPrimitiveClass(primitiveType)
}
}
func (self *ClassLoader) loadPrimitiveClass(className string) {
class := &Class{
accessFlags: ACC_PUBLIC,
name: className,
loader: self,
initStarted: true,
}
class.jClass = self.classMap["java/lang/Class"].NewObject()
class.jClass.extra = class
self.classMap[className] = class
}
jlClassClass := self.LoadClass("java/lang/Class")
中LoadClass()的实现如下
func (self *ClassLoader) LoadClass(name string) (class *Class) {
if class, ok := self.classMap[name]; ok { // 检测缓存中有没有
// alreay loaded
return class
}
if name[0] == '[' { // 判断是否是数组类
class = self.loadArrayClass(name)
} else {
class = self.loadNonArrayClass(name)
}
// 任意一个class加载时都会关联java.lang.Class的一个实例
// 使之jClass为java.lang.Class的一个实例
// 使之jClass.extra 为其自身
if jlClassClass, ok := self.classMap["java/lang/Class"]; ok {
class.jClass = jlClassClass.NewObject()
class.jClass.extra = class
}
return
}
- 对于非数组类,类的加载分为一下三步(构造了类加载器对象后,需要用它去加载一些类,没错,这是包含在classloader的构造函数中的)
- 首先找到 class 文件然后把数据读取到内存中
- 解析 class 文件 生成虚拟机可以使用的类数据 并放入方法区
- 进行链接
func (self *ClassLoader) loadNonArrayClass(name string) *Class {
data, entry := self.readClass(name) // 读取类信息
class := self.defineClass(data) // 解析类信息
link(class)
if self.verboseFlag {
fmt.Printf("[Loaded %s from %s]\n", name, entry)
}
return class
}
- 首先找到 class 文件然后把数据读取到内存中
func (self *ClassLoader) readClass(name string) ([]byte, classpath.Entry) {
data, entry, err := self.cp.ReadClass(name)
if err != nil {
panic("java.lang.ClassNotFoundException: " + name)
}
return data, entry
}
- 解析 class 文件 生成虚拟机可以使用的类数据 并放入方法区
解释一下,方法区,它是运行时数据区的一块逻辑区域,由多个线程共享。方法区主要存放从class文件获取的类信息。此外,类变量也存放在方法区中。当Java虚拟机第一次使用某个类时,它会搜索类路径,找到相应的class文件,然后读取并解析class文件,把相关信息放进方法区。至于方法区到底位于何处,是固定大小还是动态调整,是否参与垃圾回收,以及如何在方法区内存放类数据等,Java虚拟机规范并没有明确规定。
func (self *ClassLoader) defineClass(data []byte) *Class {
class := parseClass(data)
class.loader = self // 绑定加载器
resolveSuperClass(class)
resolveInterfaces(class)
self.classMap[class.name] = class // 注册登记
return class
}
func parseClass(data []byte) *Class {
cf, err := classfile.Parse(data)
if err != nil {
panic(err)
}
return newClass(cf)
}
简单来看一下class文件的解析(现在我们在构造JVM, 在构造类加载器的过程中,需要去加载class文件)
type ClassFile struct {
magic uint32
minorVersion uint16
majorVersion uint16
constantPool ConstantPool // 常量池
accessFlags uint16
thisClass uint16
superClass uint16
interClass uint16
interfaces []uint16
fields []*MemberInfo
methods []*MemberInfo
attributes []AttributeInfo
}
解析函数的实现很简单:
func Parse(classData []byte) (cf *ClassFile, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
}
} ()
cr := &ClassReader{classData}
cf = &ClassFile{}
cf.read(cr)
return
}
解析后对应字段赋值
func (self *ClassFile) read(reader *ClassReader) {
self.readAndCheckMagic(reader)
self.readAndCheckVersion(reader)
self.constantPool = readConstantPool(reader)
self.accessFlags = reader.readUint16()
self.thisClass = reader.readUint16()
self.superClass = reader.readUint16()
self.interfaces = reader.readUint16s()
self.fields = readMembers(reader, self.constantPool)
self.methods = readMembers(reader, self.constantPool)
self.attributes = readAttributes(reader, self.constantPool)
}
需要注意的是 readConstantPool
、readMembers
以及 readAttributes
的实现
- 常量池 常量池类似于SymbolTable (现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件,现在来解析其中的常量池)
常量池占据了class文件很大一部分数据,里面存放着各式各样的常量信息,包括数字和字符串常量、类和接口名、字段和方法名,等等
可以把常量池中的常量分为两类:字面量(literal)和符号引用(symbolic reference)。字面量包括数字常量和字符串常量,符号引用包括类和接口名、字段和方法信息等。除了字面量,其他常量都是通过索引直接或间接指向CONSTANT_Utf8_info常量
public class HelloWorld {
// 静态常量
public static final double PI = 3.14;
// 声明成员常量
final int y = 10;
public static void main(String[] args) {
// 声明局部常量
final double x = 3.3;
}
}
常量池的实现是通过type ConstantPool []ConstantInfo
构造一个ConstantInfo的Slice
type ConstantInfo interface {
readInfo(reader *ClassReader)
}
cp的构造方法:
func readConstantPool(reader *ClassReader) ConstantPool {
cpCount := int(reader.readUint16())
cp := make([]ConstantInfo, cpCount)
// 遍历类文件中的常量数据 逐个读取并保存在数组中
for i := 1;i < cpCount;i++ {
cp[i] = readConstantInfo(reader, cp)
switch cp[i].(type) {
case *ConstantLongInfo, *ConstantDoubleInfo: // Long 和 Double 占用两个位置
i++
}
}
return cp
}
func readConstantInfo(reader *ClassReader, cp ConstantPool) ConstantInfo {
tag := reader.readUint8()
c := newConstantInfo(tag, cp) // 根据不同数据类型的tag 构造不同的常量信息
c.readInfo(reader)
return c
}
经过这样readConstantPool中的循环,ConstantPool中就保存了class文件中的常量信息。通过Index可以获取对应的常量信息
func (self ConstantPool) getConstantInfo(index uint16) ConstantInfo {
if cpInfo := self[index]; cpInfo != nil {
return cpInfo
}
panic(fmt.Errorf("Invalid constant pool index: %v!", index))
}
- 字段与方法(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件,现在来解析其中的字段和方法)
func readMembers(reader *ClassReader, cp ConstantPool) []*MemberInfo {
memberCount := reader.readUint16()
members := make([]*MemberInfo, memberCount)
for i := range members {
members[i] = readMember(reader, cp)
}
return members
}
func readMember(reader *ClassReader, cp ConstantPool) *MemberInfo {
return &MemberInfo{
cp: cp,
accessFlags: reader.readUint16(),
nameIndex: reader.readUint16(),
descriptorIndex: reader.readUint16(),
attributes: readAttributes(reader, cp),
}
}
- 属性表 同样是一个AttributeInfo的Slice(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件,现在来解析其中的属性表)
type AttributeInfo interface {
readInfo(reader *ClassReader)
}
func readAttributes(reader *ClassReader, cp ConstantPool) []AttributeInfo {
attributesCount := reader.readUint16()
attributes := make([]AttributeInfo, attributesCount)
for i := range attributes {
attributes[i] = readAttribute(reader, cp)
}
return attributes
}
func readAttribute(reader *ClassReader, cp ConstantPool) AttributeInfo {
attrNameIndex := reader.readUint16()
attrName := cp.getUtf8(attrNameIndex)
attrLen := reader.readUint32()
attrInfo := newAttributeInfo(attrName, attrLen, cp) // 根据不同的attrName 构造不同的attrInfo
attrInfo.readInfo(reader)
return attrInfo
}
func newAttributeInfo(attrName string, attrLen uint32,
cp ConstantPool) AttributeInfo {
switch attrName {
// Code是变长属性,只存在于method_info结构中。Code属性中存放字节码等方法相关信息
case "Code":
return &CodeAttribute{cp: cp}
// ConstantValue是定长属性,只会出现在field_info结构中,用于表示常量表达式的值
case "ConstantValue":
return &ConstantValueAttribute{}
// Deprecated,仅起标记作用,不包含任何数据
case "Deprecated":
return &DeprecatedAttribute{}
// Exceptions是变长属性,记录方法抛出的异常表
case "Exceptions":
return &ExceptionsAttribute{}
// LineNumberTable属性表存放方法的行号信息
case "LineNumberTable":
return &LineNumberTableAttribute{}
// LocalVariableTable属性表中存放方法的局部变量信息
case "LocalVariableTable":
return &LocalVariableTableAttribute{}
// SourceFile是可选定长属性,只会出现在ClassFile结构中,用于指出源文件名
case "SourceFile":
return &SourceFileAttribute{cp: cp}
// Synthetic,仅起标记作用,不包含任何数据
case "Synthetic":
return &SyntheticAttribute{}
default:
return &UnparsedAttribute{attrName, attrLen, nil}
}
}
解析过class文件后,调用newClass() 来构造该类(现在我们在构造JVM,解析了classpath,正在构造classloader,读取了class文件,现在使用这些类信息来构造class对象)
type Class struct {
accessFlags uint16 // 访问级别
name string // thisClassName
superClassName string // 父类名
interfaceNames []string // 实现的接口名
constantPool *ConstantPool // 运行时常量池指针
fields []*Field // 类字段
methods []*Method // 类方法
sourceFile string // 源文件
loader *ClassLoader // 类加载器
superClass *Class // 父类指针
interfaces []*Class // 实现的接口表
instanceSlotCount uint // 运行时数据占用的槽量
staticSlotCount uint // 静态数据占用的槽量
staticVars Slots // 静态数据
initStarted bool // 表示类的<clinit>方法是否已经开始执行
jClass *Object // java.lang.Class的变量引用
}
func newClass(cf *classfile.ClassFile) *Class {
class := &Class{}
class.accessFlags = cf.AccessFlags()
class.name = cf.ClassName()
class.superClassName = cf.SuperClassName()
class.interfaceNames = cf.InterfaceNames()
class.constantPool = newConstantPool(class, cf.ConstantPool()) // 加载运行时常量池
class.fields = newFileds(class, cf.Fields()) // 加载运行时字段
class.methods = newMethods(class, cf.Methods()) // 加载运行时方法
class.sourceFile = getSourceFile(cf)
return class
}
对于这段代码,需要注意的有几个:newConstantPool() newFields() newMethods()
newConstantPool(class, cf.ConstantPool())
类对象的运行时常量池指针
func newConstantPool(class *Class, cfCp classfile.ConstantPool) *ConstantPool {
cpCount := len(cfCp)
consts := make([]Constant, cpCount)
rtCp := &ConstantPool{class, consts}
for i := 1;i < cpCount;i++ {
cpInfo := cfCp[i]
switch cpInfo.(type) {
case *classfile.ConstantIntegerInfo:
intInfo := cpInfo.(*classfile.ConstantIntegerInfo)
consts[i] = intInfo.Value()
// ...
case *classfile.ConstantStringInfo:
stringInfo := cpInfo.(*classfile.ConstantStringInfo)
consts[i] = stringInfo.String()
case *classfile.ConstantClassInfo:
classInfo := cpInfo.(*classfile.ConstantClassInfo)
consts[i] = newClassRef(rtCp, classInfo) // 当前类的一个引用
case *classfile.ConstantFieldrefInfo:
fieldrefInfo := cpInfo.(*classfile.ConstantFieldrefInfo)
consts[i] = newFieldRef(rtCp, fieldrefInfo)
case *classfile.ConstantMethodrefInfo:
methodrefInfo := cpInfo.(*classfile.ConstantMethodrefInfo)
consts[i] = newMethodRef(rtCp, methodrefInfo)
case *classfile.ConstantInterfaceMethodrefInfo:
methodrefInfo := cpInfo.(*classfile.ConstantInterfaceMethodrefInfo)
consts[i] = newInterfaceMethodRef(rtCp, methodrefInfo)
default:
// TODO
}
}
return rtCp
}
不难看出,其实这个函数就是解析了classfile中的ConstantPool对象,将其中保存的数据进行映射。值得注意的是引用类型的数据:
// 根据class文件中存储的类常量创建ClassRef实例
func newClassRef(cp *ConstantPool, classInfo *classfile.ConstantClassInfo) *ClassRef {
ref := &ClassRef{}
ref.cp = cp
ref.className = classInfo.Name()
return ref
}
func newFieldRef(cp *ConstantPool, refInfo *classfile.ConstantFieldrefInfo) *FieldRef{
ref := &FieldRef{}
ref.cp = cp
ref.copyMemberRefInfo(&refInfo.ConstantMemberrefInfo)
return ref
}
Method 与 Interface_methodRef的实现与field_ref的基本一致,都是拷贝一些必要的信息给引用
func (self *MemberRef) copyMemberRefInfo(refInfo *classfile.ConstantMemberrefInfo) {
self.className = refInfo.ClassName()
self.name, self.descriptor = refInfo.NameAndDescriptor()
}
- newFields 类对象的运行时字段
type ClassMember struct {
accessFlags uint16 // 访问级别
name string // 类名
descriptor string // 描述符
class *Class // 类指针
}
type Field struct {
ClassMember
constValueIndex uint
slotId uint
}
// classfile.MemberInfo 转换为 Fileds
func newFileds(class *Class, cfFields []*classfile.MemberInfo) []*Field {
fields := make([]*Field, len(cfFields))
for i, cfField := range cfFields {
fields[i] = &Field{}
fields[i].class = class
fields[i].copyMemberInfo(cfField)
fields[i].copyAttributes(cfField)
}
return fields
}
- newMethods 类对象的运行时方法
type Method struct {
ClassMember
maxStack uint
maxLocals uint
code []byte
exceptionTable ExceptionTable
lineNumberTable *classfile.LineNumberTableAttribute
argSlotCount uint
}
func newMethods(class *Class, cfMethods []*classfile.MemberInfo) []*Method {
methods := make([]*Method, len(cfMethods))
for i, cfMethod := range cfMethods {
methods[i] = newMethod(class, cfMethod)
}
return methods
}
构造好类对象后,我们要把这个类注册到类加载器的classMap中,提醒一下,我们现在还没有走出classLoader的构造函数
func (self *ClassLoader) loadNonArrayClass(name string) *Class {
data, entry := self.readClass(name) // 读取类信息
class := self.defineClass(data) // 解析类信息
link(class)
if self.verboseFlag {
fmt.Printf("[Loaded %s from %s]\n", name, entry)
}
return class
}
func (self *ClassLoader) defineClass(data []byte) *Class {
class := parseClass(data)
class.loader = self // 绑定加载器
resolveSuperClass(class)
resolveInterfaces(class)
self.classMap[class.name] = class // 注册登记
return class
}
以上函数是类解析过程的第二步, 看一下resolveSuperClass(class)和resolveInterfaces(class)的实现
func resolveSuperClass(class *Class) {
if class.name != "java/lang/Object" { // Object没有父类
// 递归向上加载类
class.superClass = class.loader.LoadClass(class.superClassName)
}
}
func resolveInterfaces(class *Class) {
interfaceCount := len(class.interfaceNames)
if interfaceCount > 0 {
class.interfaces = make([]*Class, interfaceCount)
for i, interfaceName := range class.interfaceNames {
// 逐个加载接口类
class.interfaces[i] = class.loader.LoadClass(interfaceName)
}
}
}
- 进行链接
func link(class *Class) {
prepare(class)
}
func prepare(class *Class) {
calcInstanceFieldSlotIds(class)
calcStaticFieldSlotIds(class)
allocAndInitStaticVars(class)
}
计算类实例数据以及静态数据占用的槽量
// calculate how many Instantce vars do we need
func calcInstanceFieldSlotIds(class *Class) {
slotId := uint(0)
if class.superClass != nil {
slotId = class.superClass.instanceSlotCount
}
for _, field := range class.fields {
if !field.IsStatic() {
field.slotId = slotId
slotId++
if field.isLongOrDouble() {
slotId++
}
}
}
class.instanceSlotCount = slotId // how many slot we need
}
// calculate how many STATIC vars do we need
func calcStaticFieldSlotIds(class *Class) {
slotId := uint(0)
for _, field := range class.fields {
if field.IsStatic() {
field.slotId = slotId
slotId++
if field.isLongOrDouble() {
slotId++
}
}
}
class.staticSlotCount = slotId // how many slot we need
}
分配内存并初始化静态变量
func allocAndInitStaticVars(class *Class) {
class.staticVars = newSlots(class.staticSlotCount) // allocate mem for static vars
for _, field := range class.fields {
if field.IsStatic() && field.IsFinal() {
initStaticFinalVar(class, field) // init the satic final vars
}
}
}
func initStaticFinalVar(class *Class, field *Field) {
vars := class.staticVars
cp := class.constantPool
cpIndex := field.ConstValueIndex()
slotId := field.SlotId()
if cpIndex > 0 {
switch field.Descriptor() {
case "Z", "B", "C", "S", "I":
val := cp.GetConstant(cpIndex).(int32)
vars.SetInt(slotId, val)
// ...
}
}
}
- 对于数组类 (我们正在构造JVM对象,构造了classpath后,正在构造classloader,构造classloader时需要去预加载一些class,在这个过程中分为非数组类和数组类)
func (self *ClassLoader) LoadClass(name string) (class *Class) {
if name[0] == '[' { // 判断是否是数组类
class = self.loadArrayClass(name)
} else {
class = self.loadNonArrayClass(name)
}
}
func (self *ClassLoader) loadArrayClass(name string) *Class {
// int[]{1, 2, 3, 4} 这就是一个数组类 [I
class := &Class{
accessFlags: ACC_PUBLIC,
name: name,
loader: self,
initStarted: true, // 数组类不需要初始化
superClass: self.LoadClass("java/lang/Object"),
interfaces: []*Class{ // 实现了 以下两个类
self.LoadClass("java/lang/Cloneable"),
self.LoadClass("java/io/Serializable"),
},
}
self.classMap[name] = class
return class
}
类的实例化
为加载好的对象创建一个java/lang/Class的实例作为类信息
func (self *ClassLoader) loadBasicClasses() {
jlClassClass := self.LoadClass("java/lang/Class")
for _, class := range self.classMap {
if class.jClass == nil {
class.jClass = jlClassClass.NewObject()
class.jClass.extra = class
}
}
}
func (self *Class) NewObject() *Object {
return newObject(self)
}
type Object struct {
class *Class
data interface{}
extra interface{} // 记录Object结构体实例的额外信息
}
func newObject(class *Class) *Object {
return &Object{
class: class,
data: newSlots(class.instanceSlotCount), // 分配类实例内存
}
}
Slot是实际存储数据的槽
type Slot struct {
num int32 // 数值
ref *Object // 引用
}
type Slots []Slot
func newSlots(slotCount uint) Slots {
if slotCount > 0 {
return make([]Slot, slotCount)
}
return nil
}
类加载器构造结束
经过漫长的类加载过程,我们终于构造好了类加载器,我们做了什么呢?
- 我们构造了classpath对象,获取了class文件的数据源
- 构造了classloader对象,但它里面暂时还没有数据
- 使用它去加载了java/lang/Class以及一些基本数据类型
- 类加载器会去从classpath中找到这些类的class文件,读取、解析、链接
- 预加载过这些类后,为每一个类的jlClass创建一个java/lang/Class实例作为类信息对外暴露
jvm的线程
上面,我们构造了类加载器,作为JVM的最后一部分,我们来看一下mainThread主线程
func newJVM(cmd *Cmd) *JVM {
cp := classpath.Parse(cmd.XjreOption, cmd.cpOption)
classLoader := heap.NewClassLoader(cp, cmd.verboseClassFlag)
return &JVM{
cmd: cmd,
classLoader: classLoader,
mainThread: rtda.NewThread(),
}
}
由于我们只是实现一个简单的玩具,并不考虑实现多线程。
/*
JVM
Thread
pc
Stack
Frame
LocalVars
OperandStack
**/
type Thread struct {
pc int
stack *Stack
}
func NewThread() *Thread {
return &Thread{
stack: newStack(1024), // 最多存放1024个栈帧
}
}
jvm中一个抽象的栈
type Stack struct {
maxSize uint
size uint
_top *Frame
}
func newStack(maxSize uint) *Stack {
return &Stack{
maxSize: maxSize,
}
}
初始化
func (self *JVM) start() {
self.initVM()
self.execMain()
}
func (self *JVM) initVM() {
vmClass := self.classLoader.LoadClass("sun/misc/VM")
base.InitClass(self.mainThread, vmClass)
interpret(self.mainThread, self.cmd.verboseInstFlag)
}
类的加载已经讲过了,现在去初始化类
// 初始化类
func InitClass(thread *rtda.Thread, class *heap.Class) {
// 标志已经初始化
class.StartInit()
// 构造函数压栈
scheduleClinit(thread, class)
// 递归构造父类
initSuperClass(thread, class)
}
func scheduleClinit(thread *rtda.Thread, class *heap.Class) {
clinit := class.GetClinitMethod()
if clinit != nil {
// exec <clinit>
newFrame := thread.NewFrame(clinit)
thread.PushFrame(newFrame)
}
}
func initSuperClass(thread *rtda.Thread, class *heap.Class) {
if !class.IsInterface() { // not a interface
superClass := class.SuperClass()
if superClass != nil && !superClass.InitStarted() {
InitClass(thread, superClass)
}
}
}
来说一下 GetClinitMethod 和 Frame的几个操作
func (self *Class) GetClinitMethod() *Method {
return self.getMethod("<clinit>", "()V", true)
}
func (self *Class) getMethod(name, descriptor string, isStatic bool) *Method {
for c := self; c != nil; c = c.superClass {
for _, method := range c.methods {
if method.IsStatic() == isStatic &&
method.name == name && method.descriptor == descriptor {
return method
}
}
}
return nil
}
从当前类及其父类中寻找名为"“描述符为”()V"的方法
- Frame的操作
func (self *Thread) NewFrame(method *heap.Method) *Frame {
return newFrame(self, method)
}
type Frame struct {
lower *Frame
localVars LocalVars
operandStack *OperandStack
thread *Thread
method *heap.Method
nextPC int // the next instruction after the call
}
func newFrame(thread *Thread, method *heap.Method) *Frame {
return &Frame {
thread: thread,
method: method,
localVars: newLocalVars(method.MaxLocals()),
operandStack: newOperandStack(method.MaxStack()),
}
}
这里的Frame是一个链表的节点,我们使用链表来模拟一个Stack. 每个Frame就是一个函数栈帧,函数栈帧中又包含了局部变量和操作数栈。
PushFrame相当于将当前函数栈帧压入线程栈顶,这样执行时就会操作该栈内的数据
interpret
函数在下一节进行解说
执行字节码
在class文件文件中会有Code AttributeInfo 其中包含了jvm字节码,通过解析这些字节码,翻译成对应的指令,再逐步执行这些我们已经实现好了的指令,就实现了一个有效的虚拟机
func (self *JVM) start() {
self.initVM()
self.execMain()
}
func (self *JVM) execMain() {
className := strings.Replace(self.cmd.class, ".", "/", -1)
mainClass := self.classLoader.LoadClass(className)
mainMethod := mainClass.GetMainMethod()
if mainMethod == nil {
fmt.Printf("Main method not found in class %s\n", self.cmd.class)
return
}
argsArr := self.createArgsArray()
frame := self.mainThread.NewFrame(mainMethod)
frame.LocalVars().SetRef(0, argsArr)
self.mainThread.PushFrame(frame)
interpret(self.mainThread, self.cmd.verboseInstFlag)
}
执行Main方法时,先加载当前主类,然后找到main方法,为main方法新建一个函数栈帧,设置函数本地变量表的第0位为cli传入的参数
func interpret(thread *rtda.Thread, logInst bool) {
defer catchErr(thread)
loop(thread, logInst)
}
func catchErr(thread *rtda.Thread) {
if r := recover(); r != nil {
logFrames(thread)
panic(r)
}
}
func loop(thread *rtda.Thread, logInst bool) {
reader := &base.BytecodeReader{}
for {
frame := thread.CurrentFrame() // 当前函数栈帧
pc := frame.NextPC()
thread.SetPC(pc)
// decode
reader.Reset(frame.Method().Code(), pc)
opcode := reader.ReadUint8()
inst := instructions.NewInstruction(opcode)
inst.FetchOperands(reader)
frame.SetNextPC(reader.PC())
if logInst {
logInstruction(frame, inst)
}
// execute
inst.Execute(frame)
if thread.IsStackEmpty() {
break
}
}
}
当loop循环开始后,位于mainThread栈顶的是mainFrame,向后移动PC后,构造Instruction对象,开始解码当前函数的的字节码,然后执行该指令的Execute()
func NewInstruction(opcode byte) base.Instruction {
switch opcode {
case 0x00:
return nop
// ... 很多指令 200条左右
case 0xbb:
return &NEW{}
case 0xbc:
return &NEW_ARRAY{}
case 0xbd:
return &ANEW_ARRAY{}
case 0xbe:
return arraylength
case 0xbf:
return athrow
case 0xc0:
return &CHECK_CAST{}
case 0xc1:
return &INSTANCE_OF{}
case 0xc4:
return &WIDE{}
case 0xc5:
return &MULTI_ANEW_ARRAY{}
case 0xc6:
return &IFNULL{}
case 0xc7:
return &IFNONNULL{}
case 0xc8:
return &GOTO_W{}
case 0xfe:
return invoke_native
default:
panic(fmt.Errorf("Unsupported opcode: 0x%x!", opcode))
}
}