--by Caesar
对象以Heap的方式统一管理,ref以索引方式读取定长的Heap头。
永久对象:HeapBody存储在NVM中,并以TLV的方式连接。Heap头存储指向HeapBody的偏移。
临时对象:HeapBody为预置的全局数组。Heap头不存储偏移,偏移由索引前的所有临时对象的空间的和计算得出。由于获取偏移的计算量比较大,所以使用Cache的方式来加速(见附录1)
永久对象:删除Heap头。并删除指向的HeapBody,如果待删的HeapBody前后都有空块,则自动合并成为一个大空块。
临时对象:删除Heap头。全局数组对应偏移的数据,自动向前移动对象长度个字节。
系统可以调用JCSystem.isObjectDeletionSupported 和JCSystem.requestObjectDeletion 进行运行态的垃圾回收请求。
在Java环境中,垃圾对象指的是未被有效对象引用的对象。在JavaCard环境里面,有效对象被定义为如下几种对象:
1.JCRE系统对象。
2.指明不能被垃圾回收的普通对象。
3.包的static image对象。
4.应用的instance实例对象。
5.Java运行栈和临时变量区中的引用类型变量指向的对象。
6.被以上几种对象直接或者间接引用的对象。
凡是不符合以上有效对象描述的对象,都被称为垃圾对象。
基于对垃圾对象的描述,可以看到,如果要做垃圾回收,必须先确定所有的有效对象。由于分析运行栈内数据类型比较困难,所以我们通常选择在Java运行栈为空的时候启动垃圾回收。由于对象的引用关系是深层和交叉的,所以无法用固定维度的循环来处理。遍历的逻辑为:
1.申请一个大缓存空间,每个对象的引用对应两个bit的标志位。初始为00.
2.遍历全部的pkg索引,获得对应的static image的对象引用,并将对应的标志位设置为10.
3.遍历全部的app索引,获得对应的instance实例对象引用,并将对应的标志位设置为10.
4.遍历全部的obj索引,获得所有表明不能垃圾回收的对象的引用,并将对应的标志位设置为10.
5.循环遍历缓冲区,找到所有标志位为10对应的对象,获得该对象直接引用的所有对象的引用(见附录2),并将对应的标志设置为1X。之后将自己的标志位设置成11。持续循环,直到再也找不到标志位为10的对象。
6.当前缓冲区内,标志位为11的对象即为有效对象,标志位为00的对象即为垃圾对象。可以以此为基础进行进一步的处理。
其中第一标志位代表对象的有效性,第二标志位代表对象的引用关系是否已经展开。以上的逻辑就是设置有效对象的根节点,然后全部展开。循环结束的标志为找不到10意味着,没有有效对象没有被展开了。
其中1-4对应方法:bool MMGC_ObjMap_Init(P_FRAM(U08) mapbuf, U16 maplen)
其中5对应方法:void MMGC_ObjMap_Traverse(P_FRAM(U08) mapbuf)
其中6对应方法:bool MMGC_ObjMap_Operate(U08 u8Scope, U08 u8Condition, U08 u8OpType, P_FRAM(U08) mapbuf, HEAPREF ref)
可以看到,垃圾回收的实现:当用户程序调用API试图垃圾回收的时候,系统不马上操作(运行栈不为空),而是记录flg。直到下次process入口点方法执行之前,判断flg,重置flag,并依照以上的步骤,进行对象的删除。
if(g_bGCFlag && VMFRAME_ISEMPTY())
{
g_bGCFlag = false;
MMGC_ObjMap_Init(g_pau8TempBuf,MMOBJ_TEMP_BUF_SIZE);
MMGC_ObjMap_Traverse(g_pau8TempBuf);
MMGC_ObjMap_Operate(MMGC_OP_SCOPE_NOTIN_MAP, MMGC_OP_CONDITION_NONE, MMGC_OP_TYPE_DEL, g_pau8TempBuf, MM_REF_NULL);
}
在用户调用GlobalPlatform的delete命令试图删除一个应用的时候,首先要判断在所有的逻辑通道内,该应用是否为激活状态,然后就要做依赖性判断,原则就是该应用的任何对象(ObjectOwner=thisApp)均不能被其它应用的对象直接或者间接的引用。或者被包的static image直接或者间接的引用。(安全域作为特殊的应用,依赖关系过于复杂,不建议支持删除)
结合垃圾回收过程,该过程的逻辑相似。只需要在第四步之后,将该app对应的instance实例对象和AID对象重置为00。即从有效对象中删除该app的入口点对象和关联的JCRE对象。遍历之后,判断有效对象中是否存在该应用的对象。如果不存在,证明该应用的对象没有被其它的有效对象直接或者间接的引用,可以删除。反之,不能删除。而删除的过程和垃圾回收一样,删除所有状态位为00的无效对象就可以了。注意,AID作为关联的JCRE对象,不参与引用关系判断,但是参与回收。如下:
MMGC_ObjMap_Init(apdu_buffer, SYSTP_ISO_APDU_BUFFER_SIZE);
MMGC_ObjMap_ResetApp(apdu_buffer, arApp);
MMGC_ObjMap_Traverse(apdu_buffer);
//ref by other object
if(MMGC_ObjMap_Operate(MMGC_OP_SCOPE_IN_MAP, MMGC_OP_CONDITION_OWNER_EQ, MMGC_OP_TYPE_RET, apdu_buffer, arApp))
{
SysExcp_ThrowISO(ISO_SW_CONDITIONS_NOT_SATISFIED);
}
//delete appref
SYSReg_Delete(arApp);
//delete all obj of app
MMGC_ObjMap_Operate(MMGC_OP_SCOPE_NOTIN_MAP, MMGC_OP_CONDITION_NONE, MMGC_OP_TYPE_DEL, apdu_buffer, MM_REF_NULL);
在用户调用GlobalPlatform的delete命令试图删除一个包的时候,首先要判断在所有的逻辑通道内,属于该包的应用是否为激活状态,该包是否被其它的包显式的import。然后就要做依赖性判断,原则就是该包的任何对象(Context=thisPkg)均不能被其它包应用的对象直接或者间接的引用。或者被其它包的static image直接或者间接的引用。
结合垃圾回收和应用删除过程,该过程的逻辑相似。只需要在第四步之后,将该pkg对应的static image对象以及所有应用的instance实例对象和AID对象重置为00。即从有效对象中删除该pkg的入口点对象和关联的JCRE对象。遍历之后,判断有效对象中是否存在该包的对象。如果不存在,证明该应用的对象没有被其它的有效对象直接或者间接的引用,可以删除。反之,不能删除。而删除的过程和垃圾回收一样,删除所有状态位为00的无效对象就可以了。注意,AID作为关联的JCRE对象,不参与引用关系判断,但是参与回收。如下:
//perform dependency checks
MMGC_ObjMap_Init(apdu_buffer, SYSTP_ISO_APDU_BUFFER_SIZE);
//
MMGC_ObjMap_ResetPkg(apdu_buffer, prPkg, (bool)(iso_p2==0x80));
//
MMGC_ObjMap_Traverse(apdu_buffer);
if(MMGC_ObjMap_Operate(MMGC_OP_SCOPE_IN_MAP, MMGC_OP_CONDITION_CONTEXT_EQ, MMGC_OP_TYPE_RET, apdu_buffer, prPkg))
{
SysExcp_ThrowISO(ISO_SW_CONDITIONS_NOT_SATISFIED);
}
if(iso_p2 == 0x80)
{
//delete appref
DeleteAppInPkg(prPkg);
}
//delete pkgref
MMHeap_Delete(prPkg);
//delete all package object MMGC_ObjMap_Operate(MMGC_OP_SCOPE_NOTIN_MAP, MMGC_OP_CONDITION_NONE, MMGC_OP_TYPE_DEL, apdu_buffer, MM_REF_NULL);
鉴于永久对象的存储方式,不适合进行大规模的搬移整理。系统的最大下载能力不是所有空块的空间总和,而是以HeapBody的最大TLV块为限制的。所以要谨防空间的碎片化。空间的碎片化有如下几种情况产生。
用户行为中的交叉下载和删除操作:
用户下载包A,包B,删除A,就将导致包B分割了两个空块,最大空间将不再是 Max-B。
系统行为中的对象:
用户下载包A,运行A的应用过程在调用API的时候产生了JCRE系统对象。(比 如某些static ref),删除A。这个系统对象将分割两个空块,最大空间将不再是Max。
我们无法控制用户行为,只能尽量减少API运行时产生对象。无field的对象则无此限制。并建议用户在大规模下载之前,最好删除所有的应用和包,并重新下载和安装。