kvm类加载-001

开篇

在JVM中,类加载的过程如图所示:

在这里插入图片描述

  • 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象。
  • 连接,连接又包含三块内容:验证、准备、初始化。
  1. 验证,文件格式、元数据、字节码、符号引用验证;
  2. 准备,为类的静态变量分配内存,并将其初始化为默认值;
  3. 解析,把类中的符号引用转换为直接引用
  • 初始化,为类的静态变量赋予正确的初始值。

而在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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值