设为首页 收藏本站
查看: 898|回复: 0

[经验分享] 你是否了解KVM的常量池

[复制链接]
累计签到:5 天
连续签到:1 天
发表于 2015-10-10 10:38:38 | 显示全部楼层 |阅读模式
  
  
  在class文件中,“常量池”是最复杂也最值得关注的内容。   
Java是一种动态连接的语言,常量池的作用非常重要,常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值还,还包含一些以文本形式出现的符号引用,比如:     
类和接口的全限定名;     
字段的名称和描述符;     
方法和名称和描述符。     
在C语言中,如果一个程序要调用其它库中的函数,在连接时,该函数在库中的位置(即相对于库文件开头的偏移量)会被写在程序中,在运行时,直接去这个地址调用函数;     
而在Java语言中不是这样,一切都是动态的。编译时,如果发现对其它类方法的调用或者对其它类字段的引用的话,记录进class文件中的,只能是一个文本形式的符号引用,在连接过程中,虚拟机根据这个文本信息去查找对应的方法或字段。     
所以,与Java语言中的所谓“常量”不同,class文件中的“常量”内容很非富,这些常量集中在class中的一个区域存放,一个紧接着一个,这里就称为“常量池”。     
常量池由多条“常量池项”组成,每一个常量池项又由两部分组成,这里分别称为“常量池项头”和“常量池项体”。     
常量池项头表明常量池项的类型,常量池项共分为11种类型,分别为:     
常量池项类型     
值     
说明     
CONSTANT_Utf8     
1     
UTF-8编码的Unicode字符串     
CONSTANT_Integer     
3     
int型常量     
CONSTANT_Float     
4     
Float型常量     
CONSTANT_Long     
5     
Long型常量     
CONSTANT_Double     
6     
double型常量     
CONSTANT_Class     
7     
对一个class的符号引用     
CONSTANT_String     
8     
String型常量     
CONSTANT_Fieldref     
9     
对一个字段的符号引用     
CONSTANT_Methodref     
10     
对一个类方法的符号引用     
CONSTANT_InterfaceMedthodref     
11     
对一个接口方法的符号引用     
CONSTANT_NameAndType     
12     
对名称和类型的符号引用     
常量池项体中存放的就是对应的常量数据,比如各种数值型的常量或者字符串等等。     
以下介绍kvm中的常量池是如何组织起来的。     
数据结构:     
在KVM的头文件kvm/vmcommon/h/pool.h中,有以下对常量池项类型的定义:     
#define CONSTANT_Utf8 1     
#define CONSTANT_Integer 3     
#define CONSTANT_Float 4     
#define CONSTANT_Long 5     
#define CONSTANT_Double 6     
#define CONSTANT_Class 7     
#define CONSTANT_String 8     
#define CONSTANT_Fieldref 9     
#define CONSTANT_Methodref 10     
#define CONSTANT_InterfaceMethodref 11     
#define CONSTANT_NameAndType 12     
以及常量池项体结构的定义:     
union constantPoolEntryStruct {     
struct {     
unsigned short classIndex;     
unsigned short nameTypeIndex;     
} method; /* Also used by Fields */     
CLASS clazz;     
INTERNED_STRING_INSTANCE String;     
cell *cache; /* Either clazz or String */     
cell integer;     
long length;     
NameTypeKey nameTypeKey;     
NameKey nameKey;     
UString ustring;     
};     
class文件中,常量池项有很多种类,每一个常量池项的大小都不同,而对于常量池的使用又是如此之多,最好能够使用数组来索引,这样可以提高效率,所以KVM里使用union来代表一个常池项,union的每一项是常量池项的一种可能的数据类型,这样每一项都有了相同的大小,可以构造数组。     
显然,这个数组就将是常量池的核心内容,那么这个数组放在哪里呢?就在下面这个结构中:     
struct constantPoolStruct {     
union constantPoolEntryStruct entries[1];     
};     
这就是常量池。这个常量池的设计很有意思:     
1、这个结构体中只有一个指针,指向一个常量池项体数组,数组中元素的个数是常量池项数+1,数组中的第一项(即序号为0的那一项)不是实际的常量池项体,而是存放了常量池项的数目,即表明了数组中接下来的元素数。要取得数组的长度信息,只有一个办法,就是读数组的第一个元素,为不造成空指针错误,所以constantPoolStruct在定义的时候就要保证数组的第0个元素必须存在,所以上面的entries在定义时就被指定为长度为1的数组。     
单纯从数据结构的设计角度来看,我认为constantPoolStruct的设计并不是很清晰,使用数组的第一个无素来表示数组的长度多少一点显得混乱,明明可以在constantPoolStruct的结构里增加一个变量来表明数组长度,这样不是更清晰吗?之所以这样做,我想也是与class文件中常量池的设计惯例有关。在class文件中, constant_pool紧跟在constant_pool_count之后,而constant_pool_count = constant_pool中实际的项数+1,相当于constant_pool_count也把自己当成了常量池中的第一项。     
由此可见,KVM的常量池设计与class文件如出一辙。     
2、常量池项体以一个union来表示,而union不带有自身类型的信息,如何知道一个常量池项的类型呢?     
在一个class文件的常量池被载入后,生成了constantPoolStruct结构体的实例,在其中constantPoolEntryStruct数组的最后一项之后,一定会跟随一个字节数组,这个数组中的每一个字节就是一个“常量池项头”,长度与实际的常量池项数相同,即constant_pool_count-1,在这个字节中就指明了相应常量池项的类型。     
程序实现:     
构造常量池的代码段主要在kvm/vmcommon/src/loader.c的loadConstantPool()函数中,函数原形如下:     
static POINTERLIST     
loadConstantPool(FILEPOINTER_HANDLE ClassFileH, INSTANCE_CLASS CurrentClass);     
两个参数分别为类文件的句柄以及当前被载入类的指针。     
这个函数的总体流程如下:     
1- 循环读取文件中常量池中所有项,把,把各项内容存入临时数组RowPool中;(L649~L740)     
2- 计算常量池所占空间大小(以constantPoolEntryStruct枚举体数计),并申请常量池空间;(L742~L757)     
3- 循环读取暂存在RowPool中的常量信息,为常量池赋值。     
其中第2步值得一看,记算空间大小的那一行如下:     
int tableSize = numberOfEntries + ((numberOfEntries + (4 - 1)) >> 2);     
一个constantPoolEntryStruct枚举体的大小为4,前面讲过,在constantPoolEntryStruct数组的后要跟有一个字节数组来存放常量池项的类型信息,即每一个constantPoolEntryStruct要对应1个字节的常量池项头,所以当以constantPoolEntryStruct枚举体数为单位给常量池项头数组申请空间时,需要向4字节对齐,每多1~4个常量池项头,就要多申请一个constantPoolEntryStruct。这一句就是这个意思。     
loadConstantPool函数执行过程中,会把新生成的常量池指针赋给CurrentClass->constPool,这样,这个类实例中就有完整的常量池了。
             版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-125005-1-1.html 上篇帖子: kvm虚拟机在线迁移 下篇帖子: how to enable kvm trace events
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表