kvm类加载-005

本文讲述kvm加载字段的部分.在jvm规范中,每个字段(Field)都由field_info结构所定义,在同一个Class文件中,不会有两个字段同时具有相同的字段名和描述符.field_info结构格式如下:

field_info {  
u2 access_flags;  
u2 name_index;  
u2 descriptor_index;  
u2 attributes_count;  
attribute_info attributes[attributes_count]; } 

field_info结构各项的说明如下:

  • access_flags

    access_flags项的值是用于定义字段被访问权限和基础属性的掩码标志。access_flags的取值范围和相应含义如图:
    在这里插入图片描述

    在这里插入图片描述
    字段如果带有ACC_SYNTHETIC标志,则说明这个字段不是由源码产生的,而是由编译器自动产生的。

    字段如果被标有ACC_ENUM标志,这说明这个字段是一个枚举类型成员。

    Class文件中的字段可以被设置多个上图中的标记。不过有些标记是互斥的,一个字段最多只能设置ACC_PRIVATE, ACC_PROTECTED,和ACC_PUBLIC(JLS §8.3.1)三个标志中的一个,也不能同时设置标志ACC_FINAL和ACC_VOLATILE(JLS §8.3.1.4)。

    接口中的所有字段都具有ACC_PUBLIC,ACC_STATIC和ACC_FINAL标记,也可能被设置ACC_SYNTHETIC标记,但是不能含有上图中的其它符号标记了(JLS §9.3)。 在上图中没有出现的access_flags项的值为扩充而预留,在生成的Class文件中应被设置成0,Java虚拟机实现也应该忽略它们。

    而在KVM中,识别的访问标记中不包含ACC_SYNTHETIC,ACC_ENUM

  • name_index

    name_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,表示一个有效的字段的非全限定名(§4.2.2)。

  • descriptor_index

    descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,表示一个有效的字段的描述符(§4.3.2)。

  • attributes_count

    attributes_count的项的值表示当前字段的附加属性(§4.7)的数量。

  • attributes[]

    attributes表的每一个成员的值必须是attribute(§4.7)结构,一个字段可以有任意个关联属性。 本规范所定义的field_info结构中,attributes表可出现的成员有:

    ConstantValue(§4.7.2), Synthetic(§4.7.8), Signature(§4.7.9), Deprecated(§4.7.15), RuntimeVisibleAnnotations(§4.7.16) 和 RuntimeInvisibleAnnotations(§4.7.17)。

    Java虚拟机实现必须正确的识别和读取field_info结构的attributes表中的ConstantValue(§4.7.2)属性。如果Java虚拟机实现支持版本号为49.0或更高的Class文件,那么它必须正确的识别和读取这些Class文件中的Signature(§4.7.9), RuntimeVisibleAnnotations(§4.7.16) 和RuntimeInvisibleAnnotations(§4.7.17)结构。 所有Java虚拟机实现都必须默认忽略field_info结构中attributes表所不可识别的成员。本规范中没有定义的属性不可影响Class文件的语义,它们只能提供附加描述信息(§4.7.1)。

    而在KVM中,只处理了ConstantValue.

加载字段

在KVM 中加载字段的实现,代码量比较大,其步骤如下:

  1. 计算要分配内存的大小.如果字段数量为0,则不进行后续处理.其中,计算内存大小的代码如下:

     unsigned short fieldCount  = loadShort(ClassFileH);
     //  ((sizeof(struct fieldTableStruct) + 3) >> 2 + (n - 1) * (sizeof(struct fieldStruct) + 3) >> 2)
     int fieldTableSize = SIZEOF_FIELDTABLE(fieldCount);
    

    fieldTableStruct的定义如下:

    struct fieldTableStruct { 
    long length; // 存放字段表的数量
    struct fieldStruct fields[1];
    };
    

    fieldStruct 的定义如下:

    /*  FIELD */
    struct fieldStruct {
        NameTypeKey nameTypeKey;
        long        accessFlags; /* 访问标记*/
        INSTANCE_CLASS ofClass;  /* 指向该对象的实例 */
        union { 
            long  offset;        /* 非静态字段使用 */
            void *staticAddress; /*  静态字段才使用  */
        } u;
    };
    
  2. 在内存中进行分配:

        #if USESTATIC
         fieldTable = (FIELDTABLE)callocObject(fieldTableSize, GCT_NOPOINTERS);
     #else
         fieldTable = (FIELDTABLE)callocPermanentObject(fieldTableSize);
     #endif
         fieldTable->length = fieldCount;
         CurrentClass->fieldTable = fieldTable;
         
    
  3. 复制数据

    1. 读取access_flags

    2. 读取name_index

    3. 读取descriptor_index

    4. 如果是静态的话,则尝试读取其ConstantValue.并将其保存至fieldStruct.u.offset中.代码如下:

        		      static void
        		loadStaticFieldAttributes(FILEPOINTER_HANDLE ClassFileH,
        		                          INSTANCE_CLASS CurrentClass,
        		                          FIELD thisField,
        		                          POINTERLIST_HANDLE StringPoolH)
        		{
        		    int cpIndex = 0;
        		    unsigned short attrCount = loadShort(ClassFileH);
        		    unsigned short attrIndex;
        		
        		    /* See if the field has any attributes in the class file */
        		    for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
        		        unsigned short attrNameIndex = loadShort(ClassFileH);
        		        unsigned long  attrLength    = loadCell(ClassFileH);
        		        const char*    attrName     = getUTF8String(StringPoolH, attrNameIndex);
        		
        		        /* Check if the attribute represents a constant value index */
        		        if (strcmp(attrName, "ConstantValue") == 0) {
        		            if (attrLength != 2) {
        		                raiseExceptionWithMessage(ClassFormatError,
        		                    KVM_MSG_BAD_CONSTANTVALUE_LENGTH);
        		            }
        		            if (cpIndex != 0) {
        		                raiseExceptionWithMessage(ClassFormatError,
        		                    KVM_MSG_DUPLICATE_CONSTANTVALUE_ATTRIBUTE);
        		            }
        		            /* Read index to a constant in constant pool */
        		            cpIndex = loadShort(ClassFileH);
        		            if (cpIndex == 0) {
        		                raiseExceptionWithMessage(ClassFormatError,
        		                    KVM_MSG_BAD_CONSTANT_INDEX);
        		            }
        		        } else {
        		            /* Unrecognized attribute; read the bytes to /dev/null */
        		            skipBytes(ClassFileH, attrLength);
        		        }
        		    }
        		    thisField->u.offset = cpIndex;
        }
      

    这部分的代码如下:

    // 3. 复制数据
     for (index = 0; index < fieldCount; index++) {
         // 读取access\_flags
         unsigned short accessFlags =
             loadShort(ClassFileH) & RECOGNIZED_FIELD_FLAGS;
         //  读取name\_index
         unsigned short nameIndex   = loadShort(ClassFileH);
         // 读取 descriptor\_index
         unsigned short typeIndex   = loadShort(ClassFileH);
         bool_t isStatic = (accessFlags & ACC_STATIC) != 0;
         FIELD thisField;
    
         START_TEMPORARY_ROOTS
             DECLARE_TEMPORARY_ROOT(const char *, fieldName,
                                    getUTF8String(StringPoolH, nameIndex));
             DECLARE_TEMPORARY_ROOT(const char *, signature,
                                    getUTF8String(StringPoolH, typeIndex));
             NameTypeKey result;
    
             verifyFieldFlags(accessFlags, CurrentClass->clazz.accessFlags);
             verifyName(fieldName, LegalField);
             verifyFieldType(signature);
             // 计算nameKey
             result.nt.nameKey =
                 change_Name_to_Key(&fieldName, 0, strlen(fieldName));
             // 计算typeKey 
             result.nt.typeKey =
                 change_FieldSignature_to_Key(&signature, 0, strlen(signature));
    
             ASSERTING_NO_ALLOCATION
                 thisField = &CurrentClass->fieldTable->fields[index];
    
     	#if INCLUDEDEBUGCODE
     	            if (traceclassloadingverbose) {
     	                fprintf(stdout, "Field '%s' loaded\n", fieldName);
     	            }
     	#endif /* INCLUDEDEBUGCODE */
    
                 /* Check if the field is double length, or is a pointer type.
                  * If so set the appropriate bit in the word */
                 switch (signature[0]) {
                    case 'D': case 'J':   accessFlags |= ACC_DOUBLE;   break;
                      case 'L': case '[':   accessFlags |= ACC_POINTER;  break;
                 }
    
                 /*  保存变量 */
                 thisField->nameTypeKey = result;
                 thisField->ofClass     = CurrentClass;
                 thisField->accessFlags = accessFlags;
    
                 if (isStatic) {
                 	// 如果是静态的话,则尝试读取其ConstantValue.并将其保存至fieldStruct.u.offset中
                     loadStaticFieldAttributes(ClassFileH, CurrentClass,
                                               thisField, StringPoolH);
                     if (accessFlags & ACC_POINTER) {
                         staticPtrCount++;
                     } else {
                         staticNonPtrCount += (accessFlags & ACC_DOUBLE) ? 2 : 1;
                     }
                 } else {
                     ignoreAttributes(ClassFileH, StringPoolH);
                 }
             END_ASSERTING_NO_ALLOCATION
         END_TEMPORARY_ROOTS
     }
    

    这里有一点要说一下:

    在KVM中,会将静态字段分配在一个持久代,其数据结构为: POINTERLIST.而在KVM,静态字段又分为静态指针字段,静态非指针字段.而在分配时,就为一个技巧,将静态指针字段分配在静态非指针字段的前面,并且将POINTERLIST的长度设置为静态指针字段的数量,这样在垃圾收集的时候,就会只收集静态指针字段.

  4. 处理静态字段。

        if (staticPtrCount > 0 || staticNonPtrCount > 0) {
             // 1. 计算要分配的大小,并进行分配.
             int staticsSize = SIZEOF_POINTERLIST(staticNonPtrCount+staticPtrCount);
             POINTERLIST statics = (POINTERLIST)callocPermanentObject(staticsSize);
             
             
             // 所有静态指针,分配在非指针的静态字段的前面
             void **nextPtrField = (void **)statics->data;
             void **nextNonPtrField = nextPtrField + staticPtrCount;
     
             /*  将POINTERLIST的长度设置为静态指针字段的数量,这样在垃圾收集的时候,就会只收集静态指针字段  */
             statics->length = staticPtrCount;
             CurrentClass->staticFields = statics;
     
             ASSERTING_NO_ALLOCATION
                 CONSTANTPOOL ConstantPool = CurrentClass->constPool;
                 if (USESTATIC) {
                     /* Otherwise, this is in permanent memory and won't move */
                     fieldTable = CurrentClass->fieldTable;
                 }
     
                 // 处理静态字段
                 FOR_EACH_FIELD(thisField, fieldTable)
                     long accessFlags = thisField->accessFlags;
                     unsigned short cpIndex;
                     // 如果该字段不是静态,则跳过
                     if (!(accessFlags & ACC_STATIC)) {
                         continue;
                     }
                     cpIndex = (unsigned short)(thisField->u.offset);
                     if (thisField->accessFlags & ACC_POINTER) {
                         /*  这种情况只能是string,这里是对string的特殊处理,对于其他对象,数组,都是在<cinit>方法中初始化的*/
                         thisField->u.staticAddress = nextPtrField;
                         if (cpIndex != 0) {
                             verifyConstantPoolEntry(CurrentClass, cpIndex,
                                                     CONSTANT_String);
                             *(INTERNED_STRING_INSTANCE *)nextPtrField =
                                 CP_ENTRY(cpIndex).String;
                         }
                         nextPtrField++;
                     } else {
                         thisField->u.staticAddress = nextNonPtrField;
                         if (cpIndex != 0) {
                             unsigned char tag;
                             switch(thisField->nameTypeKey.nt.typeKey) {
                                 case 'B': case 'C': case 'Z': case 'S': case 'I':
                                     tag = CONSTANT_Integer;
                                     break;
                                 case 'F':
     #if !IMPLEMENTS_FLOAT
                                     fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
     #endif
                                     tag = CONSTANT_Float;
                                     break;
                                 case 'D':
     #if !IMPLEMENTS_FLOAT
                                     fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
     #endif
                                     tag = CONSTANT_Double;
                                     break;
                                 case 'J':
                                     tag = CONSTANT_Long;
                                     break;
                                 default:
                                     raiseExceptionWithMessage(ClassFormatError,
                                         KVM_MSG_BAD_SIGNATURE);
                             }
                             verifyConstantPoolEntry(CurrentClass, cpIndex, tag);
                             // 设置静态变量的值
                             if (accessFlags & ACC_DOUBLE) {
                                 /* Initialize a double or long */
                                 CONSTANTPOOL_ENTRY thisEntry = &CP_ENTRY(cpIndex);
                                 unsigned long hiBytes, loBytes;
                                 hiBytes = (unsigned long)(thisEntry[0].integer);
                                 loBytes = (unsigned long)(thisEntry[1].integer);
                                 SET_LONG_FROM_HALVES(nextNonPtrField,
                                                      hiBytes, loBytes);
                             } else {
                                 *(cell *)nextNonPtrField =
                                    CP_ENTRY(cpIndex).integer;
                             }
                         }
                         nextNonPtrField += (accessFlags & ACC_DOUBLE) ? 2 : 1;
                     }
                 END_FOR_EACH_FIELD
             END_ASSERTING_NO_ALLOCATION
         }
    
  5. 检查是否有重复字段

    if (fieldCount >= 2) {
         if (USESTATIC) {
             /* Otherwise, this is in permanent memory and won't move */
             fieldTable = CurrentClass->fieldTable;
         }
         ASSERTING_NO_ALLOCATION
             /* Check to see if there are two fields with the same name/type */
             FIELD firstField = &fieldTable->fields[0];
             FIELD lastField = firstField + (fieldCount - 1);
             FIELD outer, inner;
             for (outer = firstField; outer < lastField; outer++) {
                 for (inner = outer + 1; inner <= lastField; inner++) {
                      // 如果有相同的,则抛出异常
                     if (outer->nameTypeKey.i == inner->nameTypeKey.i) {
                         raiseExceptionWithMessage(ClassFormatError,
                             KVM_MSG_DUPLICATE_FIELD_FOUND);
                     }
                 }
             }
         END_ASSERTING_NO_ALLOCATION
     }
    

总结一下:

kvm中定义了fieldTableStruct,用来保存字段的信息.同时,对应静态字段,又分配了大小为staticNonPtrCount+staticPtrCount的POINTERLIST.并且由CurrentClass中的staticFields指向该POINTERLIST.代码如下:

CurrentClass->staticFields = statics;

对于这个POINTERLIST使用了一个技巧,所有静态指针分配在非指针的静态字段的前面。这样在垃圾收集的时候,就会只收集静态指针字段.

而对于非静态字段,只是保存了nameTypeKey,ofClass,accessFlags。代码如下:

thisField->nameTypeKey = result;
thisField->ofClass     = CurrentClass;
thisField->accessFlags = accessFlags;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值