四、类加载之准备阶段

前面已经把类文件加载了出来,并且将字节流转化成了易用的数据结构,下面就是类的验证和准备阶段了。验证阶段在书中忽略了,所以这里只包括准备阶段。

准备阶段主要是给类变量分配空间,赋上默认值,static final变量赋上初始值。如下所示,用slot数组保存静态变量。

type Class struct {
	。。。
	staticSlotCount    uint
	staticVars         Slots //类静态变量
}

Slot定义如下,静态变量都保存在slot中,32位的类型如int,float,boolean,char,引用等都占用一个slot,64位的long和double占用两个slot。Slots是Slot的数组类型,主要封装了Slot的设置和查询操作。

type Slot struct {
	num int32
	ref *heap.Object
}

type Slots []Slot

func (this Slots) SetInt(index uint, val int32) {
	this[index].num = val
}

func (this Slots) GetInt(index uint) int32 {
	return this[index].num
}

func (this Slots) SetFloat(index uint, val float32) {
	this[index].num = int32(math.Float32bits(val))
}

func (this Slots) GetFloat(index uint) float32 {
	return math.Float32frombits(uint32(this[index].num))
}

func (this Slots) SetLong(index uint, val int64) {
	this[index].num = int32(val)
	this[index+1].num = int32(val >> 32)
}

func (this Slots) GetLong(index uint) int64 {
	low := uint32(this[index].num)
	high := uint32(this[index+1].num)
	return int64(high)<<32 | int64(low)

}

func (this Slots) SetDouble(index uint, val float64) {
	bits := int64(math.Float64bits(val))
	this.SetLong(index, bits)
}

func (this Slots) GetDouble(index uint) float64 {
	bits := uint64(this.GetLong(index))
	return math.Float64frombits(bits)
}

func (this Slots) SetRef(index uint, ref *heap.Object) {
	this[index].ref = ref
}

func (this Slots) GetRef(index uint) *heap.Object {
	return this[index].ref
}

上面对Slots的操作需要Slot在Slots中的位置index,即字段占用了第几个Slot,所以在Field结构体中加上SlotId字段表示该字段占用第几个Slot。

type Field struct {
	ClassMember
	constValueIndex uint16
	slotId          uint
}

那么SlotId怎么确定呢?可以按照field在class中的顺序来分配,需要注意的是,父类也可能有静态变量。所以可以按照继承关系,把父类的字段放到前面,子类的SlotId从父类的最后一个SlotId开始计数,代码如下

// 为每个静态变量分配slotId,并统计slot个数
func calcStaticFieldId(class *Class) {
	slotId := uint(0)
	if class.superClass != nil {
		slotId = class.superClass.staticSlotCount
	}
	for _, field := range class.fields {
		if !field.isStatic() {
			continue
		}
		field.slotId = slotId
		slotId++
		if field.isLongOrDouble() {
			slotId++
		}
	}
	class.staticSlotCount = slotId
}

确定好字段在Slot数组中的位置之后,就可以把字段的默认值或初始值放到Slots里了。因为Go默认会分配默认值,所以默认值不需要我们额外赋值,只需要把static final的初始值放到slots即可。常量的初始值的index保存在field的ConstValue的Attribute中。

func initStaticVars(class *Class) {
	class.staticVars = newSlots(class.staticSlotCount) // 因为go语言本身就会给slot分配默认值,所以默认值不需要再分配
	for _, field := range class.fields {
		if field.isStatic() && field.isFinal() { // static final的变量会有初始值,分配在const value常量中
			initStaticFinalVars(class, field)
		}
	}
}

func initStaticFinalVars(class *Class, field *Field) {
	staticVars := class.staticVars
	poolIndex := field.constValueIndex
	constPool := class.constantPool
	descriptor := field.descriptor
	slotId := field.slotId

	if descriptor == "Z" || descriptor == "B" || descriptor == "C" || descriptor == "S" || descriptor == "I" {
		staticVars.SetInt(slotId, constPool.GetInteger(poolIndex))
		return
	}

	if descriptor == "J" {
		staticVars.SetLong(slotId, constPool.GetLong(poolIndex))
		return
	}

	if descriptor == "D" {
		staticVars.SetDouble(slotId, constPool.GetDouble(poolIndex))
		return
	}

	if descriptor == "Ljava/lang/String" {
		// TODO 常量池暂不支持string
	}

}

 


版权声明:本文为oBuShanQing原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。