kvm在启动时的步骤如下:

  1. 创建ROM镜像,此处为宏,定义在j2me_cldc/kvm/VmCommon/h/garbage.h
  2. 初始化FPU
  3. 初始化异步I/0系统
  4. 初始化本地代码
  5. 初始化vm,此处为宏,定义在j2me_cldc/kvm/VmUnix/h/machine_md.h
  6. 初始化全局变量
  7. 初始化性能统计的变量,定义在 j2me_cldc/kvm/VmCommon/h/profiling.h
  8. 初始化内存系统
  9. 初始化内部hash 表 --> 本文介绍该步骤
  10. 初始化inline cache --> 本文介绍该步骤
  11. 初始化类加载接口
  12. 初始化VM所需要的内部类
  13. 初始化class文件验证器
  14. 初始化事件处理系统
  15. 加载主类
  16. 解析参数
  17. 初始化多线程
  18. 初始化系统类
  19. 解释执行

初始化HASH表

此处的代码如下:

void InitializeHashtables() { 
    if (!ROMIZING) { 
        createHashTable(&UTFStringTable, UTF_TABLE_SIZE); // 256
        createHashTable(&InternStringTable, INTERN_TABLE_SIZE); // 32
        createHashTable(&ClassTable, CLASS_TABLE_SIZE); // 32
    }
}

可以知道,在kvm启动过程中,会创建3个hash表: UTFStringTable, InternStringTable, ClassTable.
这里创建过程中调用了createHashTable。其代码如下:

void
createHashTable(HASHTABLE *tablePtr, int bucketCount) { 
    // 1. 计算分配大小
    int objectSize = SIZEOF_HASHTABLE(bucketCount);
    // 2. 进行分配
    HASHTABLE table = (HASHTABLE)callocPermanentObject(objectSize);
    // 3. 赋值
    table->bucketCount = bucketCount;
    *tablePtr = table;
}

其中第一步如下:

SIZEOF_HASHTABLE(bucketCount) = (StructSizeInCells(HashTable) + (n - 1)) = ((sizeof(struct HashTable) + 3) >> 2) + (n-1) // 注意,此处计算的是要分配多少个字节

HashTable的定义如下:

typedef struct HashTable {
    long bucketCount;     /*  bucket的数量*/
    long count;           /* 在table中有多少个元素 */
    cell *bucket[1];     /* bucket数组*/
} *HASHTABLE;

第二步,在持久代中分配对象,其代码如下:

cell*
callocPermanentObject(long size) {
    cell* result;
#if ENABLE_HEAP_COMPACTION
    result = PermanentSpaceFreePtr - size;
    PermanentSpaceFreePtr = result;
    if (result < CurrentHeapEnd) {
        /* We have to expand permanent memory  此时需要扩展持久代*/
        cell* newPermanentSpace = CurrentHeapEnd;
        do {
            newPermanentSpace = PTR_OFFSET(newPermanentSpace, -0x800); // 递减2Kbytes
        } while (newPermanentSpace > result);
        /* We need to expand permanent memory to include the memory between
         * newPermanentSpace and CurrentHeapEnd
         */

        /* We pass GC a request that is larger than it could possibly
         * fulfill, so as to force a GC.
         * 进行full gc
         */
        garbageCollect(AllHeapEnd - AllHeapStart);

        if (newPermanentSpace < (cell*)FirstFreeChunk + 2 * HEADERSIZE) {// 如果内存不足,则抛出异常
            raiseExceptionWithMessage(OutOfMemoryError,
                KVM_MSG_UNABLE_TO_EXPAND_PERMANENT_MEMORY);
        } else {
            int newFreeSize =
                (newPermanentSpace - (cell*)FirstFreeChunk - HEADERSIZE);
            memset(newPermanentSpace, 0,
                   PTR_DELTA(CurrentHeapEnd, newPermanentSpace));
            CurrentHeapEnd = newPermanentSpace;
            // 由于进行压缩后,java heap 中只有一块可使用空间,而这块空间大小就是newFreeSize
            FirstFreeChunk->size = newFreeSize << TYPEBITS;
        }
    }
    return result;
#else /* ENABLE_HEAP_COMPACTION */
    result = callocObject(size, GCT_NOPOINTERS);
    OBJECT_HEADER(result) |= STATICBIT;
    return result;
#endif /* ENABLE_HEAP_COMPACTION */
}

同样,关于gc的问题,我们后续有文章介绍.对于当前情况,如图所示:

kvm 加载光驱 kvm怎么启动_缓存

初始化inline cache

此步骤的代码如下:

void
InitializeInlineCaching(void)
{
    /* Align the cache area so that accessing static variables is safe
     * regardless of the alignment settings of the compiler
     */
    // 1. 在持久代中分配InlineCache, INLINECACHESIZE = 128.因此,会分配长度为128的数组
    InlineCache = 
        (ICACHE)callocPermanentObject(SIZEOF_ICACHE*INLINECACHESIZE+1);
    InlineCachePointer = 0;
    InlineCacheAreaFull = FALSE;
    // 2. 清零
    memset(InlineCache, 0, (SIZEOF_ICACHE*INLINECACHESIZE+1)*sizeof(CELL));
}

InlineCache的定义如下:

struct icacheStruct {
    cell* contents;  /*   指向实际要执行方法 */
    BYTE* codeLoc;   /*  指向引用内联缓存项的代码位置*/
    short origParam; /*  原始字节码的参数,其值= codeLoc+1 */
    BYTE  origInst;  /*  原始字节码 */
};

关于这点,有以下说明:

  • icache是用于存储单个内联缓存项的结构。整个内联缓存只是一个icache数组。
  • 一旦内联缓存区域满了,我们就开始重新使用从icache区域开始的最旧条目。引用重新使用的icache条目的方法的代码将替换为原始(预内联缓存)代码。换句话说,整个内联缓存过程是完全可逆和可重复的。
  • 为了避免垃圾收集问题,我们不在内联缓存中存储任何动态堆指针!这确保了我们可以在垃圾收集期间忽略整个内联缓存区域