`
guoxinzz
  • 浏览: 431234 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

你是否了解KVM的常量池

 
阅读更多

在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,这样,这个类实例中就有完整的常量池了。

分享到:
评论

相关推荐

    kvm虚拟机安装介绍,KVM

    kvm虚拟机安装介绍 KVM 虚拟机的管理工具  准确来说,KVM 仅仅是 Linux 内核的一个模块。管理和创建完整的 KVM 虚拟机,需要更多的辅助工具 QEMU-KVM 在 Linux 系统中,首先我们可以用 modprobe 系统工具去加载...

    KVM创建存储池、存储卷

    systemctl status libvirtd 查看服务启没启动 命令介绍 ...解析:pool-define-as dirpool(池名字) dir(类型) –target /kvm-vm/dirpool/(定义目录地址) pool-build dirpool 构建池 pool-start dirp

    KVM虚拟化高级实战课程-网盘链接提取码下载 .txt

    KVM虚拟化高级课程包括了KVM课程概述,KVM虚拟网络高级特性,KVM共享存储的动态迁移,Linux HA群集体系结构,基于NFS的KVM群集构建,基于iSCSI的KVM群集构建,基于DRBD的KVM群集构建,P2V、V2V迁移,KVM嵌套虚拟化,...

    img2kvm.zip

    img转kvm工具 (此工具文件名无后缀) 使用方法: 把需要的img镜像文件和img2kvm文件上传到主机的root目录 输入以下两行代码(假设iso文件名为openwrt.img): chmod +x img2kvm ./img2kvm openwrt.img 100 vm...

    Mastering KVM Virtualization pdf

    This book doesn’t just show you how to virtualize with KVM – it shows you how to do it well. Written to make you an expert on KVM, you’ll learn to manage the three essential pillars of scalability,...

    KVM配置及Openstack kvm优化

    kvm环境搭建、kvm配置命令、虚拟化管理、openstack kvm优化

    linux下KVM虚拟化总结

    kvm是linux内核虚拟化,文中指出了kvm虚拟化环境的安装搭建及使用工具。

    kvm安装以及ova镜像转换

    kvm安装以及ova镜像转换

    KVM实现机制与原理

    介绍KVM的内部实现机制与原理,是了解KVM的必备资料。

    Proxmox VE导入OpenWrt/LEDE固件的工具——img2kvm

    若是你的PVE主机能访问Internet,可以直接下载到目录“/usr/bin”或“/usr/local/bin”下,下面是将img2kvm下载到目录“/usr/local/bin”以及增加其可执行权限的命令: wget -P /usr/local/bin ...

    kvm管理系统(shell脚本)

    包括安装,添加网卡、硬盘、内存,创建删除KVM存储池,创建删除KVM虚拟机快照,一键部署网桥模式

    kvm虚拟化视频教程.zip

    KVM-day1 01-什么是kvm虚拟化.mp4 02-安装kvm虚拟机.mp4 03-virsh的日常管理命令上.mp4 04-virsh的日常管理命令下.mp4 05-kvm虚拟机开机自启和console登录.mp4 kvm-day2 01-kvm虚拟机磁盘格式转换.mp4 02-kvm虚拟机...

    基于KVM_QEMU与Libvirt的虚拟化资源池构建

    基于KVM_QEMU与Libvirt的虚拟化资源池构建,

    KVM huawei.7z

    KVM 华为服务器管理客户端软件 使用电脑连接到服务器的管理口 或者管理交换机 即可连接服务器进行维护,管理

    PVE专用的img转kvm文件

    img转kvm工具 ...用winscp登录你的虚拟机IP,把img2kvm和镜像文件一股脑的放置到root下,然后输入以下代码: chmod +x img2kvm ./img2kvm 文件名.img 100 vm-100-disk-1 上面的100表示虚拟机编号

    kvm虚拟机性能优化方案

    kvm性能优化方案

    云计算实验报告一(KVM与Docker安装使用)

    任务:通过KVM的官方网站https://www.linux-kvm.org/,在Linux系统中下载并安装使用KVM,进一步了解KVM的原理。 3) Docker是目前最流行的轻量级虚拟化解决方案,并开始在越来越多的场合中替代传统的虚拟机技术。 ...

    Linux内核虚拟化KVM详解

    Linux内核虚拟化KVM详解

    kvm_client_windows.zip

    KVM client for windows ,

Global site tag (gtag.js) - Google Analytics