模板是c++的一种特性,允许函数或者类通过泛型(generic types)的形式表现或者运行。模板可以使得函数或类在对应不同的类型(types)的时候正常工作,而无需为每一种类型分别写一份代码。
在HotSpot VM中定义了一些模板类,有了这些模板类,我们就可以和Java一样进行泛型编程。HotSpot VM中定义了Dictionary和SymbolTable等用来存储类和字符串等内容的Hash表,这些容器类都使用到了模板类。
看一下具体的定义,如下:
源代码位置:/media/mazhi/sourcecode/workspace/projectjava9/learnhotspot/src/utilities/hashtable.hpp
template <MEMFLAGS F> class BasicHashtable : public CHeapObj<F> {
...
private:
int _table_size;
HashtableBucket<F>* _buckets;
BasicHashtableEntry<F>* bucket(int i);
...
}
template <MEMFLAGS F> class BasicHashtableEntry : public CHeapObj<F> {
private:
unsigned int _hash;
BasicHashtableEntry<F>* _next;
...
}
定义了一个名称为BasicHashtable的类模板,类模板以template关键字开始,后面跟着模板参数列表。这里在模板中定义了一个非类型参数,一个非类型参数表示一个值而不是一个类型。它的定义如下:
typedef unsigned short MEMFLAGS;
其定义的非类型参数F也应用到了类模板HashtableBucket<F>中了,同样也可以应用到函数的参数和返回类型中。
通过BasicHashtable这个类模板的定义可以看出,通过此类模板生成的表或字典是通过列表来解决Hash冲突的。
下面接着看另外一个类模板,如下:
源代码位置:/media/mazhi/sourcecode/workspace/projectjava9/learnhotspot/src/utilities/hashtable.hpp
template <class T, MEMFLAGS F> class Hashtable : public BasicHashtable<F> {
protected:
...
HashtableEntry<T, F>* bucket(int i) {
return (HashtableEntry<T, F>*)BasicHashtable<F>::bucket(i);
}
}
template <class T, MEMFLAGS F> class HashtableEntry : public BasicHashtableEntry<F> {
private:
T _literal; // ref to item in table.
...
}
这次的类模板继承了BasicHashtable<F>,不过这个类模板中通过class关键字定义了一个模板类型参数T,这其实就是我们要真正往Hash表中存储的元素的类型。
SymbolTable和StringTable都使用了如上的类模板,如SymbolTable类的定义如下:
class SymbolTable : public Hashtable<Symbol*, mtSymbol> {
...
}
为类型模板参数指定了具体的类型。也就是这个Hash表中存储的元素类型为Symbol*,而在为这些元素分配内存时,将这些内存统计到mtSymbol上,这样NMT就能追踪这一部分使用内存的详细信息了。
下面看一下存储类的字典的定义,如下:
template <class T, MEMFLAGS F> class TwoOopHashtable : public Hashtable<T, F> {
...
}
class Dictionary : public TwoOopHashtable<Klass*, mtClass> {
...
}
存储Java类的字典Dictionary继承了TwoOopHashtable<Klass*,mtClass>,这是因为在Java世界中,只有类全名和类加载器才能唯一表示一个类,所以继承这个类也起到了一个见名知义的效果。
另外还有一点需要说明,就是C++对模板的分离式编译很弱。对于 BasicHashtable 中定义的bucket()函数来说,其实现是在hashtable.cpp文件中,其实严格来说,这不是函数的定义,而是生成定义的一种方案,只有在实例化时,才能生成函数定义,所以我们能在hashtable.cpp文件中看到如下类似的语句:
template class BasicHashtable<mtSymbol>;
在.cpp文件的末尾加上如上代码来显式实例化,这样我们将hashtable.cpp作为源文件加入编译时,整个程序就有了模板的显式实例化,即函数定义。否则可能报"undefined reference to"相关的错误。
本人最近准备出一个手写Hotspot VM的课程,超级硬核,从0开始写HotSpot VM,将HotSpot VM所有核心的实现全部走一遍,如感兴趣,加我速速入群。
群里可讨论虚拟机和Java性能剖析与故障诊断等话题,欢迎加入。