kvm-inlineCache

本文来详细介绍kvm中的inlineCache.

其定义如下:

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

初始化

inlineCache的初始化是在InitializeInlineCaching中实现的,其代码如下:

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; // inline cache的指针
    InlineCacheAreaFull = FALSE; // 用来指示inline cache是否已满
    // 2. 清零
    memset(InlineCache, 0, (SIZEOF_ICACHE*INLINECACHESIZE+1)*sizeof(CELL));
}

关于这点,有以下说明:

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

如何使用

kvm在解释执行的过程中,对INVOKEVIRTUAL,INVOKEINTERFACE,ANEWARRAY的处理过程中,使用到了inlineCache.

INVOKEVIRTUAL

在对INVOKEVIRTUAL的处理的过程中,有如下代码:

if (thisMethod) {
           // 缓存处理
#if ENABLEFASTBYTECODES
            if (   (cpMethod->accessFlags & (ACC_PRIVATE | ACC_FINAL))
                || (cpMethod->ofClass->clazz.accessFlags & ACC_FINAL)
               ) {
               // 如果该方法是private 或者是final 或者该方法所属的类是final的,则替换字节码为INVOKESPECIAL_FAST
                REPLACE_BYTECODE(ip, INVOKESPECIAL_FAST)
            } else {
                // 其他情况,则创建InlineCacheEntry,然后将自己码替换为INVOKEVIRTUAL_FAST,同时将操作数设为InlineCacheEntry的下标(iCacheIndex)
                int iCacheIndex;
                /* Replace the current bytecode sequence */
                // iCacheIndex = createInlineCacheEntry((cell*)cellp, ip);
                CREATE_CACHE_ENTRY((cell*)thisMethod, ip)
                /**
                 * 宏展开为: REPLACE_BYTECODE(ip, bytecode) *ip = bytecode;
                 */
                REPLACE_BYTECODE(ip, INVOKEVIRTUAL_FAST)
                putShort(ip + 1, iCacheIndex);
            }
#endif /* ENABLEFASTBYTECODES */

            TRACE_METHOD_ENTRY(thisMethod, "virtual");

            // 调用方法
            CALL_VIRTUAL_METHOD
        }

此处CREATE_CACHE_ENTRY宏展开为createInlineCacheEntry,该方法定义在j2me_cldc/kvm/VmCommon/src/cache.c中.代码如下:

int
createInlineCacheEntry(cell* contents, BYTE* originalCode)
{
    ICACHE thisICache;
    int    index;

    /* Check first if inline cache is already full 1. 如果InlineCache已满,则进行释放,从头开始释放 */
    if (InlineCacheAreaFull) {
        releaseInlineCacheEntry(InlineCachePointer);
    }

    /* Allocate new entry / reallocate old one 2. 使用新的entry或者重新使用旧的*/
    thisICache = &InlineCache[InlineCachePointer];
    index = InlineCachePointer++;

    /* Check whether the icache area is full now 3. 检查 INLINECACHE是否已满*/
    if (InlineCachePointer == INLINECACHESIZE) {
        InlineCacheAreaFull = TRUE;
        InlineCachePointer  = 0;
    }

    /* Initialize icache values 4. 初始化值*/
    thisICache->contents = contents; // 此处指向的是方法
    thisICache->codeLoc = originalCode; // 保存原有字节码的位置
    thisICache->origInst = *originalCode;// 保存原有字节码的操作码
    thisICache->origParam = getShort(originalCode+1);// 保存原有字节码的操作数

    return index;
}

其中我们来看下releaseInlineCacheEntry.其代码如下:

static void
releaseInlineCacheEntry(int index)
{
	// 1. 获得缓存
    ICACHE thisICache = &InlineCache[index];

    /* Read the pointer to the code location  */
    /* referring to this inline cache entry
     * 2. 获得该缓存的字节码原先的位置 */
    BYTE* codeLoc = (BYTE*)thisICache->codeLoc;

    /* Restore original bytecodes */
#if ENABLE_JAVA_DEBUGGER
    if (*codeLoc == BREAKPOINT) {
        replaceEventOpcode(thisICache->origInst);
    } else {
#endif
       // 3. 还原原先的字节码
       *codeLoc = thisICache->origInst;
#if ENABLE_JAVA_DEBUGGER
    }
#endif
    // 3. 还原原先的操作数
    putShort(codeLoc+1, thisICache->origParam);
}

其中可以得出以下结论:

  1. InlineCache是循环使用的,如果InlineCache已满,则会覆盖最旧的cache.
  2. InlineCache是定长的,其元素个数为128。

此后,如果再次执行该方法,则会执行对INVOKEVIRTUAL_FAST的处理,其代码如下:

#if FASTBYTECODES
SELECT(INVOKEVIRTUAL_FAST)
        /* Invoke instance method; dispatch based on dynamic class */
        /* (fast version) */

        /* Get the inline cache index */
        unsigned int iCacheIndex;
        ICACHE   thisICache;
        INSTANCE_CLASS defaultClass;
        int      argCount;
        CLASS    dynamicClass;

        /* Get the inline cache index 1. 获得cache的下标 */
        iCacheIndex = getUShort(ip + 1);

        /* Get the inline cache entry 2. 获得对应的cache entry */
        thisICache = GETINLINECACHE(iCacheIndex);

        /* Get the default method stored in cache 3.获得该cache所对应的方法 */
        thisMethod = (METHOD)thisICache->contents;

        /* Get the class of the default method 4. 获得该方法所对应的class*/
        defaultClass = thisMethod->ofClass;

        /* Get the object pointer ('this') from the operand stack */
        /* (located below the method arguments in the stack) 5. 获得该方法所对应的参数,thisObject*/
        argCount = thisMethod->argCount;
        thisObject = *(OBJECT*)(sp-argCount+1);
        CHECK_NOT_NULL(thisObject);

        /* This may be different than the default class 6. 获得thisObject所对应的class,注意此时dynamicClass可能会和缓存的class不一致*/
        dynamicClass = thisObject->ofClass;

        /* If the default class and dynamic class are the same, we can
         * just execute the method.  Otherwise a new lookup
         */
        // 7.
        if (dynamicClass != (CLASS)defaultClass) {// 7.1 如果dynamicClass和缓存的class不一致的话,则进行修正
            /* Get method table entry based on dynamic class */
            VMSAVE
			// 7.1.1 找到对应的方法
            thisMethod = lookupDynamicMethod(dynamicClass, thisMethod);
            VMRESTORE
            /* Update inline cache entry with the newly found method 7.1.2 修改对应的方法 */
            thisICache->contents = (cell*)thisMethod;
            IncrInlineCacheMissCounter(); // 此处为宏,#define IncrInlineCacheMissCounter() /**/
        }
        else IncrInlineCacheHitCounter(); // 此处为宏,#define IncrInlineCacheHitCounter() /**/

        if (!thisMethod) {
            fatalIcacheMethodError(thisICache);
        } else {
            TRACE_METHOD_ENTRY(thisMethod, "fast virtual");
            CALL_VIRTUAL_METHOD // 调用方法
        }
DONEX
#endif

INVOKEINTERFACE

在对INVOKEINTERFACE的处理的过程中,有如下代码:

#if INFREQUENTSTANDARDBYTECODES
SELECT(INVOKEINTERFACE)         /* Invoke interface method */
        unsigned int cpIndex;
        unsigned int argCount;

        /*  1.获得方法在常量池中的index  */
        cpIndex = getUShort(ip + 1);

        /*  2.获得参数的数量*/
        argCount = ip[3];

        /* Resolve constant pool reference */
        VMSAVE
		// 3. 得到对应的方法
        thisMethod = resolveMethodReference(cp_global, cpIndex, FALSE,
                                            fp_global->thisMethod->ofClass);
        VMRESTORE
        if (thisMethod) {
            INSTANCE_CLASS dynamicClass;

            /* Get "this" */
            thisObject = *(OBJECT*)(sp-argCount+1);
            CHECK_NOT_NULL(thisObject);

            
            dynamicClass = ((INSTANCE)thisObject)->ofClass;
            VMSAVE
			// 4. 在当前类中找到对应的方法
            thisMethod = lookupMethod((CLASS)dynamicClass,
                                      thisMethod->nameTypeKey,
                                      fp_global->thisMethod->ofClass);
            VMRESTORE
            if (thisMethod != NULL &&
                (thisMethod->accessFlags & (ACC_PUBLIC | ACC_STATIC)) == ACC_PUBLIC) {

#if ENABLEFASTBYTECODES
                /* 
                 * 5. 加入到缓存中 */
                int iCacheIndex;
                CREATE_CACHE_ENTRY((cell*)thisMethod, ip)
                REPLACE_BYTECODE(ip, INVOKEINTERFACE_FAST)
                putShort(ip + 1, iCacheIndex);
#endif /* ENABLEFASTBYTECODES */
                TRACE_METHOD_ENTRY(thisMethod, "interface");
                CALL_INTERFACE_METHOD
            }
        }
        VMSAVE
        fatalSlotError(cp, cpIndex);
        VMRESTORE
DONE(0)

#endif

此后,如果再次执行该方法,则会执行对INVOKEINTERFACE_FAST的处理,其代码如下:

#if FASTBYTECODES
SELECT(INVOKEINTERFACE_FAST)
        /* Invoke interface method (fast version) */

        /* Get the inline cache index */
        unsigned int iCacheIndex;
        unsigned int   argCount;
        ICACHE   thisICache;
        INSTANCE_CLASS defaultClass;
        CLASS    dynamicClass;

        /* Get the inline cache index  1. 获得在cache中的index*/
        iCacheIndex = getUShort(ip + 1);

        /* Get the argument count (specific to INVOKEINTERFACE bytecode)  2. 获得参数的数量 */
        argCount = ip[3];

        /* Get the inline cache entry 3. 获得对应的cache */
        thisICache = GETINLINECACHE(iCacheIndex);

        /* Get the default method stored in cache 4. 获得对应的方法 */
        thisMethod = (METHOD)thisICache->contents;

        /* Get the class of the default method 5. 获得缓存中的class */
        defaultClass = thisMethod->ofClass;

        /* Get the object pointer ('this') from the operand stack  6. 获得对应的this*/
        thisObject = *(OBJECT*)(sp-argCount+1);
        CHECK_NOT_NULL(thisObject);

        /* Get the runtime (dynamic) class of the object  */
        dynamicClass = thisObject->ofClass;

        /* If the default class and dynamic class are the same, DONE(1)  7. 如果this和缓存中的class,则进行修正*/
        if (dynamicClass != (CLASS)defaultClass) {
            /* Get method table entry based on dynamic class */
            VMSAVE
			// 7.1 重新查找方法
            thisMethod = lookupMethod(dynamicClass, thisMethod->nameTypeKey,
                                      fp_global->thisMethod->ofClass);
            VMRESTORE
            /* Update inline cache entry with the newly found method 7.2 修改缓存 */
            thisICache->contents = (cell*)thisMethod;
            IncrInlineCacheMissCounter();
        } else {
            IncrInlineCacheHitCounter();
        }

        if (thisMethod == NULL ||
            ((thisMethod->accessFlags & (ACC_PUBLIC | ACC_STATIC)) != ACC_PUBLIC)) {
            fatalIcacheMethodError(thisICache);
        } else {
            TRACE_METHOD_ENTRY(thisMethod, "fast interface");
            CALL_INTERFACE_METHOD; // 8. 调用方法
        }

DONEX
#endif

此处和对INVOKEVIRTUAL_FAST的处理一致,不做过多解释.

ANEWARRAY

在对ANEWARRAY的处理的过程中,有如下代码:

SELECT(ANEWARRAY)                  /* Create new array of reference type */

        /* Get the CONSTANT_Class index  1. 获得CONSTANT_Class*/
        unsigned int cpIndex = getUShort(ip + 1);
        long  arrayLength = topStack;
        CLASS elemClass;
        ARRAY_CLASS thisClass;
        ARRAY result;

        /* Get the corresponding class pointer */
        VMSAVE
		// 2. 获得数组元素对应的class
        elemClass =
           resolveClassReference(cp_global, cpIndex, fp_global->thisMethod->ofClass);
        thisClass = getObjectArrayClass(elemClass);
        VMRESTORE

#if ENABLEFASTBYTECODES
        {  // 3. 加入缓存中
            int iCacheIndex;
        /* Note that instantiateArray may change the ip on an error */
            CREATE_CACHE_ENTRY((cell*)thisClass, ip)
            REPLACE_BYTECODE(ip, ANEWARRAY_FAST)
            putShort(ip + 1, iCacheIndex);
        }
#endif /* ENABLEFASTBYTECODES */

        VMSAVE
		// 4. 初始化数组
        result = instantiateArray(thisClass, arrayLength);
        VMRESTORE
        if (result != NULL) {
            topStackAsType(ARRAY) = result;
            ip += 3;
        }
DONE_R
#endif

此处的功能是将ANEWARRAY修改为ANEWARRAY_FAST,同时将原来指向常量池中的index改为指向缓存的下标.其目的是减少resolveClassReference的操作.

则此后,再初始化该数组,则执行ANEWARRAY_FAST,其处理代码如下:

#if FASTBYTECODES
SELECT(ANEWARRAY_FAST)
        /* Create new array of reference type (fast version) */

        ARRAY result;
        /* 1. 获得cache的下标 */
        unsigned int iCacheIndex = getUShort(ip + 1);

        /* 2. 获得对应的cache */
        ICACHE thisICache = GETINLINECACHE(iCacheIndex);
        /* 3. 获得对应的类 */
        ARRAY_CLASS thisClass = (ARRAY_CLASS)thisICache->contents;
        long arrayLength = topStack;
        VMSAVE
        // 4. 初始化数组
        result = instantiateArray(thisClass, arrayLength);
        VMRESTORE
        if (result != NULL) {
            topStackAsType(ARRAY) = result;
            ip += 3;
        }
DONE(0)
#endif

cache的释放

InlineCache的释放是在KVM_Cleanup方法中,调用FinalizeInlineCaching实现的,其代码如下:

void
FinalizeInlineCaching(void)
{
	// 1. 获得最后一个下标
    int last = InlineCacheAreaFull ? INLINECACHESIZE : InlineCachePointer;
    // 2. 通过循环,依次释放之
    while (--last >= 0) {
        releaseInlineCacheEntry(last);
    }
    // 3.修改指示器
    InlineCachePointer = 0;
    InlineCacheAreaFull = FALSE;
}

以上步骤简单,同时releaseInlineCacheEntry在上文中有介绍.

总结

通过以上的分析,可以得到如下结论:

InlineCache的目的是

  1. 减少在方法调用时,查找最终要调用的方法的开销
  2. 减少在初始化对象数组时,对数组元素类型解析的开销
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值