Dalvik 虚拟机 之 哈希表使用篇
前面介绍了dvm里德哈希表提供的方法。下面就以InternedString实现为例介绍一下哈希表的使用。我们将看到dvm里哈希表的设计非常直观,其使用更是简单明了。
Interned String是指java虚拟机将某些字符串“内部化”,和不同字符串的最大区别就是在虚拟机内部永远只会存在改字符串的唯一实例。多次分配改string都将返回该字符串唯一的实例。也就是说 对于Interned string a 和b,我们可以使用比较运算符(==)来判断他们是否相同。而普通的字符串必须使用equal函数。
Java Interned String分为两类,一类是literal string。就是字符常量。比如通过以下语句得到的字符串:
String literal = "I am a literal string";
另一类是用户调用 java.lang.String.intern() 得到的。 与字符常量不同,该类字符串只能是弱引用的(weakly referenced)。
DVM interned string 实现 在 dalvik/vm/Intern.h dalvik/vm/Intern.cpp 里。 下面将看看它是如何使用hash来实现的。
/*
* Prep string interning.
*/
bool dvmStringInternStartup()
{
dvmInitMutex(&gDvm.internLock);
gDvm.internedStrings = dvmHashTableCreate(256, NULL);
if (gDvm.internedStrings == NULL)
return false;
gDvm.literalStrings = dvmHashTableCreate(256, NULL);
if (gDvm.literalStrings == NULL)
return false;
return true;
}
首先是在启动函数里,调用dvmHashTableCreate来创建两个哈希表,一个存放 interned string,一个存放literal string。初始的表大小为256。单元释放函数为NULLL。
/*
* Chuck the intern list.
*
* The contents of the list are StringObjects that live on the GC heap.
*/
void dvmStringInternShutdown()
{
if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) {
dvmDestroyMutex(&gDvm.internLock);
}
dvmHashTableFree(gDvm.internedStrings);
gDvm.internedStrings = NULL;
dvmHashTableFree(gDvm.literalStrings);
gDvm.literalStrings = NULL;
}
这是相反的过程,调用dvmHashTableFree清理掉所有哈希表单元,然后释放掉哈希表本省。
static StringObject* lookupString(HashTable* table, u4 key, StringObject* value)
{
void* entry = dvmHashTableLookup(table, key, (void*)value,
dvmHashcmpStrings, false);
return (StringObject*)entry;
}
搜索字符串更简单,只需要调用哈希表搜索函数dvmHashTableLookup。传入的哈希表可能是前面提到的 gDvm.internedStrings或者gDvm.literalStrings。最后一个参数isAdd为false,标识这是纯粹的查找,而不需要在查找未果的情况下插入。
static StringObject* insertString(HashTable* table, u4 key, StringObject* value)
{
if (dvmIsNonMovingObject(value) == false) {
value = (StringObject*)dvmCloneObject(value, ALLOC_NON_MOVING);
}
void* entry = dvmHashTableLookup(table, key, (void*)value,
dvmHashcmpStrings, true);
return (StringObject*)entry;
}
插入字符串也是简单的调用函数dvmHashTableLookup并且传入isAdd为true(最后一个参数)。
/*
* Clear white references from the intern table.
*/
void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *))
{
/* It's possible for a GC to happen before dvmStringInternStartup()
* is called.
*/
if (gDvm.internedStrings != NULL) {
dvmLockMutex(&gDvm.internLock);
dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject);
dvmUnlockMutex(&gDvm.internLock);
}
}
调用dvmHashForeachRemove,将所有单元执行isUnmarkedObject判断,所有返回1的都被表示为不可用,从而达到了清除的目的。
我们看到,有了哈希表的支持操作后,interned string的实现非常的直观而又简洁。