开篇
在JVM中,类加载的过程如图所示:
- 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象。
- 连接,连接又包含三块内容:验证、准备、初始化。
- 验证,文件格式、元数据、字节码、符号引用验证;
- 准备,为类的静态变量分配内存,并将其初始化为默认值;
- 解析,把类中的符号引用转换为直接引用
- 初始化,为类的静态变量赋予正确的初始值。
而在kvm的实现中,类加载的状态定义为:
#define CLASS_RAW 0 /* this value must be 0 */
#define CLASS_LOADING 1
#define CLASS_LOADED 2
#define CLASS_LINKED 3
#define CLASS_VERIFIED 4
#define CLASS_READY 5
#define CLASS_ERROR -1
本系列将介绍类加载的过程.
类加载的初始化工作
这部分涉及的函数为:getRawClass,定义在j2me_cldc/kvm/VmCommon/src/class.c.在初始化系统类的过程中,就有调用函数,如下:
JavaLangObject = (INSTANCE_CLASS)getRawClass("java/lang/Object");
JavaLangClass = (INSTANCE_CLASS)getRawClass("java/lang/Class");
JavaLangString = (INSTANCE_CLASS)getRawClass("java/lang/String");
getRawClass代码如下:
CLASS
getRawClass(const char *name) {
// 如果该类已经在堆中且是debug模型,则意味着是一个错误,vm退出
if (INCLUDEDEBUGCODE && inCurrentHeap(name)) {
fatalError(KVM_MSG_BAD_CALL_TO_GETRAWCLASS);
}
// 在内存中进行分配
return getRawClassX(&name, 0, strlen(name));
}
getRawClassX代码如下:
CLASS
getRawClassX(CONST_CHAR_HANDLE nameH, int offset, int length)
{
CLASS result;
const char *start = unhand(nameH); // 获得class名称
const char *className = start + offset;
const char *firstNonBracket, *p;
int depth;
/* Find the firstNonBracket character, and the last slash in the string 1. 跳过[ */
for (p = className; *p == '['; p++) {};
firstNonBracket = p;
depth = p - className; // 计算得出数组的维度
if (depth == 0) {// 普通类
UString packageName, baseName;
for (p = className + length ; ;) {
--p;
if (*p == '/') {// 有包的情况
int packageNameLength = p - className;
int baseNameLength = (length - 1) - packageNameLength;
packageName = getUStringX(nameH, offset, packageNameLength); // 得到包名
/* p and start may no longer be valid pointers, but there
* difference is still a valid offset */
baseName = getUStringX(nameH, (p + 1) - start, baseNameLength); // 得到基本类名
break;
} else if (p == className) {// 默认包的情况
packageName = NULL;
baseName = getUStringX(nameH, offset, length);
break;
}
}
result = change_Name_to_CLASS(packageName, baseName);
return result;
} else if (depth + 1 == length) {// 基本类型的数组
/* An array of some primitive type */
return (CLASS)getArrayClass(depth, NULL, *firstNonBracket);
} else {// 对象数组
INSTANCE_CLASS baseClass;
const char *baseClassStart = firstNonBracket + 1; /* skip the 'L' */
const char *baseClassEnd = className + length - 1; /* skip final ';' */
baseClass = (INSTANCE_CLASS)
getRawClassX(nameH, baseClassStart - start,
baseClassEnd - baseClassStart);
/* The call to getArrayClass but we don't have any pointers any more. */
return (CLASS)getArrayClass(depth, baseClass, '\0');
}
}
我们具体看普通类的情况,其最终调用change_Name_to_CLASS,其代码如下:
CLASS
change_Name_to_CLASS(UString packageName, UString baseName) {
HASHTABLE table = ClassTable;
unsigned int hash;
unsigned int index;
unsigned int lastKey = 0;
CLASS *clazzPtr, clazz;
hash = stringHash(UStringInfo(baseName), baseName->length) + 37; // 1. 计算hash
if (packageName != NULL) {
hash += stringHash(UStringInfo(packageName), packageName->length) * 3;
}
index = hash % table->bucketCount; // 计算index
clazzPtr = (CLASS *)&table->bucket[index];
// 2. 查找
for (clazz = *clazzPtr; clazz != NULL; clazz = clazz->next) {
if (clazz->packageName == packageName && clazz->baseName == baseName) {
if (EXCESSIVE_GARBAGE_COLLECTION && !ASYNCHRONOUS_NATIVE_FUNCTIONS){
/* We might garbage collect, so we do */
garbageCollect(0);
}
return clazz;
}
if (lastKey == 0) {
unsigned thisKey = clazz->key;
int pseudoDepth = thisKey >> FIELD_KEY_ARRAY_SHIFT;
if (pseudoDepth == 0 || pseudoDepth == MAX_FIELD_KEY_ARRAY_DEPTH) {
lastKey = thisKey & 0x1FFF;
}
}
}
// 3. 如果没有找的话,则在堆中进行分配
if (UStringInfo(baseName)[0] == '[') {
clazz = (CLASS)callocPermanentObject(SIZEOF_ARRAY_CLASS);
} else {
clazz = (CLASS)callocPermanentObject(SIZEOF_INSTANCE_CLASS);
}
// 4. 加入到ClassTable中
clazz->next = *clazzPtr;
*clazzPtr = clazz;
clazz->packageName = packageName;
clazz->baseName = baseName;
/* The caller may change this value to be one of the values with
* depth 1 <= depth <= MAX_FIELD_KEY_ARRAY_DEPTH. */
clazz->key = lastKey == 0 ? (256 + index) : (lastKey + table->bucketCount);
// 5. 如果ClassTable中的类过多,则vm退出
if (clazz->key & ITEM_NewObject_Flag) {
fatalError(KVM_MSG_TOO_MANY_CLASS_KEYS);
}
table->count++;
return clazz;
}
同样,我们具体看分配类的情况,其最终调用callocPermanentObject,进行分配,而分配的大小是SIZEOF_INSTANCE_CLASS.其值为:
((sizeof(struct instanceClassStruct) + 3) >> 2)
这里解释一下,+3的目的是为了对齐, >> 2 是为了计算分配多少个字节.
具体解释可以参数如下链接:任意字节对齐分配内存
在分配的过程中,会将内存清零,代码如下:
memset(newPermanentSpace, 0,
PTR_DELTA(CurrentHeapEnd, newPermanentSpace));
有关内存分配的内容,会在后续文章讲解
因此,在instanceClassStruct中,其status为0,也就是为CLASS_RAW.