首先简要介绍一下class文件的结构(详细内容请参考Java虚拟机规范,在《深入Java虚拟机》一书中也有详细描述):

长度(字节)

名称

解释

4

magic

0xCAFEBABEJava文件的标识,常称为“魔数”

2

minor_version

次版本号

2

major_version

主版本号

2

constant_pool_count

常量池中项目个数

(constant_pool_count-1)*cp_item

constant_pool

常量池,其中存放了(constant_pool_count-1)个常量池项

2

access_flags

class的类型信息

2

this_class

class的全限定名的常量池项索引

2

super_class

超类的全限定名的常量池项索引

2

interfaces_count

实现/扩展的接口数

2*interfaces_count

interfaces

实现/扩展的接口名的常量池项索引

2

fields_count

字段数

fields_count* cp_item

fields

字段信息表

2

methods_count

方法数

methods_count* cp_item

methods

方法信息表

2

attributes_count

属性数

attributes_count* cp_item

attributes

属性信息表

文件系统中的一个class文件,要想成为能在虚拟上运行的Java程序的一部分,必须经过装载->连接->初始化三个步骤。其中装载是最基础的一步,它的作用是读取class文件的信息,并生成对象。下面介绍一下KVM中与类加载相关的内容是如何实现的。

 

数据结构:

在头文件kvm/vmcommon/h/class.h中定义有两个非常重要的结构体:

/*  CLASS  */
struct  classStruct {
    COMMON_OBJECT_INFO(INSTANCE_CLASS)
    UString packageName;            
/*  Everything before the final '/'  */
    UString baseName;               
/*  Everything after the final '/'  */
    CLASS   next;                   
/*  Next item in this hash table bucket  */
    unsigned 
short  accessFlags;      /*  Access information  */
    unsigned 
short  key;              /*  Class key  */
};
typedef 
struct  classStruct *          CLASS;

/*  INSTANCE_CLASS  */
struct  instanceClassStruct {
    
struct  classStruct clazz;        /*  common info  */
    
/*  And specific to instance classes  */
    INSTANCE_CLASS superClass;      
/*  Superclass, unless java.lang.Object  */
    CONSTANTPOOL constPool;         
/*  Pointer to constant pool  */
    FIELDTABLE  fieldTable;         
/*  Pointer to instance variable table  */
    METHODTABLE methodTable;        
/*  Pointer to virtual method table  */
    unsigned 
short *  ifaceTable;      /*  Pointer to interface table  */
    POINTERLIST staticFields;       
/*  Holds static fields of the class  */
    
short    instSize;                /*  The size of class instances  */
    
short  status;                    /*  Class readiness status  */
    THREAD initThread;              
/*  Thread performing class initialization  */
    NativeFuncPtr finalizer;        
/*  Pointer to finalizer  */
};
typedef 
struct  instanceClassStruct *  INSTANCE_CLASS;

classStructinstanceClassStruct这两个结构体都是与class(可能包括类和接口)有关的,但不所不同。classStruct所提供是一些“外围信息”,包括全限定名和可见性等,是一个class区分其它class的基本信息;instanceClassStruct所提供的是一些“内容信息”,是一个类本身所定义的内容,比如方法表、字段表等等。

 

程序实现:

下面来看一看在KVMinstanceClassStruct的信息是如何读取的。

一个kvm虚拟机在运行时,class来自于两种来源:一是系统类库,这些类来自KVM本身;二是用户程序,来自文件系统。下面分别介绍:

1、前文提到过,对于系统类库,KVM会先把class文件转化为C语言源代码,然后编入kvm可执行程序中,这样当使用系统类库时,kvm就不再访问外部,这一步骤叫可暂称为ROMROM过程中所做的事情之一就是生成所有系统类库中classINSTANCE_CLASS,也就是说,这些类的信息早在ROM的过程中就已准备好,使用时只要读入即可。

ROM生成的INSTANCE_CLASS信息存放在源文件tools/jcc/ROMjavaUnix.cstatic struct AllClassblocks_Struct AllClassblocks结构中,使用这一结构的代码也在同一文件中,比如:

 

INSTANCE_CLASS JavaLangString  =
     (INSTANCE_CLASS)
& AllClassblocks.java_lang_String;

这里就生成了String类对应的INSTANCE_CLASS结构。

2、对于用户定义的类,INSTANCE_CLASS信息就要从文件系统中获得。

获得类信息的主要入口函数是

void loadClassfile(INSTANCE_CLASS InitiatingClass, bool_t fatalErrorIfFail);

在文件kvm/vmcommon/src/loader.c中。其中参数InitiatingClass就是一个空的INSTANCE_CLASS结构体,在loadClassfile函数中,InitiatingClass的内容将被逐步填充。

loadClassfile中,将调用另一个重要的函数:

static void loadRawClass(INSTANCE_CLASS CurrentClass, bool_t fatalErrorIfFail);

在这个函数中,将调用下列各方法分别载入class文件中的各种信息:

函数名(参数及返回值省略)

功能

loadVersionInfo()

载入版本号

loadConstantPool()

载入常量池

loadClassInfo()

载入类型信息

loadInterfaces()

载入所实现的接口

loadFields()

载入字段表

loadMethods()

载入方法表

ignoreAttributes()

载入扩展的属性表

上表中的每一个函数都会带有一个FILEPOINTER_HANDLE型的参数,它是一个文件的句柄,这些函数就是从这个文件中顺序读取各种信息并存入INSTANCE_CLASS结构中的。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐