• 方法解析


    在ClassFileParser::parseClassFile()函数中解析完字段并完成每个字段的布局后,会继续对方法进行解析,相关的处理语句如下:

    bool            has_final_method = false;
    AccessFlags     promoted_flags;
    promoted_flags.set_flags(0);
    Array<Method*>* methods = parse_methods(access_flags.is_interface(),
                                                &promoted_flags,
                                                &has_final_method,
                                                &has_default_methods,
                                                CHECK_(nullHandle));

    调用的parse_methods()函数的实现如下:

    Array<Method*>* ClassFileParser::parse_methods(bool is_interface,
                                                   AccessFlags* promoted_flags,
                                                   bool* has_final_method,
                                                   bool* has_default_methods,
                                                   TRAPS) {
      ClassFileStream* cfs = stream();
      cfs->guarantee_more(2, CHECK_NULL);  // length
      u2 length = cfs->get_u2_fast();
      if (length == 0) {
        _methods = Universe::the_empty_method_array();
      } else {
        _methods = MetadataFactory::new_array<Method*>(_loader_data, length, NULL, CHECK_NULL);
    
        HandleMark hm(THREAD);
        for (int index = 0; index < length; index++) {
           methodHandle method = parse_method(is_interface,promoted_flags,CHECK_NULL);
    
          if (method->is_final()) {
             *has_final_method = true; // 如果定义了final方法,那么has_final_method变量的值为true
          }
          if (is_interface
        	&& !(*has_default_methods)
            && !method->is_abstract()
            && !method->is_static()
            && !method->is_private()) {
              // default method
              *has_default_methods = true; // 如果有默认的方法,则has_default_methods变量的值为true
          }
          _methods->at_put(index, method());
        }
      }
      return _methods;
    }
    

    计算出来的has_final_method与has_default_methods属性的值最终会存储到表示类的InstanceKlass对象的_misc_flags和_access_flags属性中,供其它地方使用。

    对每个Java方法调用parse_method()函数进行解析,返回表示方法的Method对象,不过Method对象需要通过methodHandle句柄来操作,所以最终返回的是methodHandle句柄,然后存储到_methods数组中。

    Java虚拟机规范规定的方法的格式如下:

    method_info {
        u2             access_flags;
        u2             name_index;
        u2             descriptor_index;
        u2             attributes_count;
        attribute_info attributes[attributes_count];
    }
    
    attribute_info {
        u2 attribute_name_index;
        u4 attribute_length;
        u1 info[attribute_length];
    }
    

    parse_method()方法会按照如上的格式读取各个属性值。首先看读取access_flags、name_index与descriptor_index的实现,如下:

    methodHandle ClassFileParser::parse_method(bool is_interface,AccessFlags *promoted_flags,TRAPS) {
      ClassFileStream* cfs = stream();
      methodHandle nullHandle;
      ResourceMark rm(THREAD);
      // Parse fixed parts
      cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count
    
      int flags = cfs->get_u2_fast();             // 也就是规范中的access_flags属性
    
      u2 name_index = cfs->get_u2_fast();         // 也就是规范中的name_index属性
      Symbol*  name = _cp->symbol_at(name_index);  
    
      u2 signature_index = cfs->get_u2_fast();    // 也就是规范中的descriptor_index属性
      Symbol*  signature = _cp->symbol_at(signature_index);
      // 读取属性的数量,也就是规范中的attributes_count属性
      u2   method_attributes_count = cfs->get_u2_fast(); 
      ...
    }
    

    接下来会在parse_method()函数中对属性进行解析,由于方法的属性较多且有些属性并不影响程序的运行,所以我们只对重要的属性Code进行解读,Code属性的格式如下:

    Code_attribute { // Code属性的格式
        u2 attribute_name_index;
        u4 attribute_length;
        u2 max_stack;
        u2 max_locals;
        u4 code_length;
        u1 code[code_length];
        u2 exception_table_length;
        {   
            u2 start_pc;
            u2 end_pc;
            u2 handler_pc;
            u2 catch_type;
        } exception_table[exception_table_length];
        u2 attributes_count;
        attribute_info attributes[attributes_count];
    }
    

    在parse_method()函数中会按照这种格式来读取信息,相关的源代码实现如下:

    while (method_attributes_count--) {
        u2      method_attribute_name_index = cfs->get_u2_fast();
        u4      method_attribute_length = cfs->get_u4_fast();
        Symbol* method_attribute_name = _cp->symbol_at(method_attribute_name_index);
    // 解析Code属性 if (method_attribute_name == vmSymbols::tag_code()) { // 读取max_stack、max_locals和code_length属性 if (_major_version == 45 && _minor_version <= 2) { max_stack = cfs->get_u1_fast(); max_locals = cfs->get_u1_fast(); code_length = cfs->get_u2_fast(); } else { max_stack = cfs->get_u2_fast(); max_locals = cfs->get_u2_fast(); code_length = cfs->get_u4_fast(); } // 读取code[code_length]数组的首地址 code_start = cfs->get_u1_buffer();
    // 跳过code_length个u1类型的数据,也就是跳转code[code_length]数组 cfs->skip_u1_fast(code_length); // 读取exception_table_length属性并处理exception_table[exception_table_length] exception_table_length = cfs->get_u2_fast(); if (exception_table_length > 0) { exception_table_start = parse_exception_table(code_length, exception_table_length, CHECK_(nullHandle)); } // 读取attributes_count属性并处理attribute_info_attributes[attributes_count]数组 u2 code_attributes_count = cfs->get_u2_fast(); // ... while (code_attributes_count--) { u2 code_attribute_name_index = cfs->get_u2_fast(); u4 code_attribute_length = cfs->get_u4_fast(); calculated_attribute_length += code_attribute_length + sizeof(code_attribute_name_index) + sizeof(code_attribute_length); if (LoadLineNumberTables && _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) { // ... } else if (LoadLocalVariableTables && _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { // ... } else if (_major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION && _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { // ... } else { // Skip unknown attributes cfs->skip_u1(code_attribute_length, CHECK_(nullHandle)); } } // end while }// end if ... } // end while

    最终会将这些分析的信息存储到Method或ConstMethod对象中。所以会创建Method或ConstMethod对象,由于方法中的各个属性已经解析完成,所以也就能得出需要为Method或ConstMethod创建的内存的大小。 

    1、创建Method与ConstMethod对象

    调用语句如下:

    // All sizing information for a Method* is finally available, now create it
    InlineTableSizes sizes(
    		  total_lvt_length,
    		  linenumber_table_length,
    		  exception_table_length,
    		  checked_exceptions_length,
    		  method_parameters_length,
    		  generic_signature_index,
    		  runtime_visible_annotations_length + runtime_invisible_annotations_length,
    		  runtime_visible_parameter_annotations_length + runtime_invisible_parameter_annotations_length,
    		  runtime_visible_type_annotations_length + runtime_invisible_type_annotations_length,
    		  annotation_default_length,
    		  0
    );
    Method* m = Method::allocate(
                   _loader_data, code_length, access_flags, &sizes,
                   ConstMethod::NORMAL, CHECK_(nullHandle));
    

    其中的InlineTableSizes类中定义了保存方法中相关属性的大小的字段,定义如下:

    class InlineTableSizes : StackObj {
    //  宏扩展后的结果如下:
    //  int _localvariable_table_length;        
    //  int _compressed_linenumber_size;        
    //  int _exception_table_length;            
    //  int _checked_exceptions_length;         
    //  int _method_parameters_length;          
    //  int _generic_signature_index;           
    //  int _method_annotations_length;         
    //  int _parameter_annotations_length;      
    //  int _type_annotations_length;           
    //  int _default_annotations_length;
      // declarations
      INLINE_TABLES_DO(INLINE_TABLE_DECLARE)
      // ...
    }
    

    之前已经介绍过ConstMethod类的内存布局结构,如下图所示。

     

    可以看到,除了方法的各属性和字节码外,其它的大小都可以通过类中的相关字段保存起来。

    调用Method::allocate()方法分配内存,Method::allocate()方法的实现如下:

    Method* Method::allocate(ClassLoaderData* loader_data,
                             int byte_code_size,
                             AccessFlags access_flags,
                             InlineTableSizes* sizes,
                             ConstMethod::MethodType method_type,
                             TRAPS) {
      assert(!access_flags.is_native() || byte_code_size == 0,
             "native methods should not contain byte codes");
      ConstMethod* cm = ConstMethod::allocate(loader_data,
                                              byte_code_size,
                                              sizes,
                                              method_type,
                                              CHECK_NULL);
    
      int size = Method::size(access_flags.is_native());
    
      return new (loader_data, size, false, MetaspaceObj::MethodType, THREAD) Method(cm, access_flags, size);
    }  
    
    ConstMethod* ConstMethod::allocate(ClassLoaderData* loader_data,
                                       int byte_code_size,
                                       InlineTableSizes* sizes,
                                       MethodType method_type,
                                       TRAPS) {
      int size = ConstMethod::size(byte_code_size, sizes);
      return new (loader_data, size, true, MetaspaceObj::ConstMethodType, THREAD) ConstMethod(
                           byte_code_size, sizes, method_type, size);
    }
    

    需要关注对Method与ConstMethod分配内存的大小。调用Method::size()方法计算Method对象所需要分配的内存大小,如下:

    int Method::size(bool is_native) {
      // If native, then include pointers for native_function and signature_handler
      int extra_bytes = (is_native) ? 2*sizeof(address*) : 0;
      int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord;
      return align_object_size(header_size() + extra_words); // 返回的是字大小
    }
    
    static int header_size() {
       return sizeof(Method)/HeapWordSize;
    }
    

    计算ConstMethod对象所需要分配的内存大小,除了ConstMethod类中表示方法的各属性占用的内存大小外,还需要传递字节码大小byte_code_size与其它属性sizes的大小,这样就可以调用ConstMethod::size()方法进行计算了,如下:

    int ConstMethod::size(int code_size,InlineTableSizes* sizes) {
      int extra_bytes = code_size;
      if (sizes->compressed_linenumber_size() > 0) {
        extra_bytes += sizes->compressed_linenumber_size();
      }
      if (sizes->checked_exceptions_length() > 0) {
        extra_bytes += sizeof(u2);
        extra_bytes += sizes->checked_exceptions_length() * sizeof(CheckedExceptionElement);
      }
      if (sizes->localvariable_table_length() > 0) {
        extra_bytes += sizeof(u2);
        extra_bytes += sizes->localvariable_table_length() * sizeof(LocalVariableTableElement);
      }
      if (sizes->exception_table_length() > 0) {
        extra_bytes += sizeof(u2);
        extra_bytes += sizes->exception_table_length() * sizeof(ExceptionTableElement);
      }
      if (sizes->generic_signature_index() != 0) {
        extra_bytes += sizeof(u2);
      }
      if (sizes->method_parameters_length() > 0) {
        extra_bytes += sizeof(u2);
        extra_bytes += sizes->method_parameters_length() * sizeof(MethodParametersElement);
      }
    
      // Align sizes up to a word.
      extra_bytes = align_size_up(extra_bytes, BytesPerWord);
    
      // One pointer per annotation array
      if (sizes->method_annotations_length() > 0) {
        extra_bytes += sizeof(AnnotationArray*);
      }
      if (sizes->parameter_annotations_length() > 0) {
        extra_bytes += sizeof(AnnotationArray*);
      }
      if (sizes->type_annotations_length() > 0) {
        extra_bytes += sizeof(AnnotationArray*);
      }
      if (sizes->default_annotations_length() > 0) {
        extra_bytes += sizeof(AnnotationArray*);
      }
    
      int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord;
      return align_object_size(header_size() + extra_words); // 内存大小的单位为字
    }
    
    static int header_size() {
        return sizeof(ConstMethod)/HeapWordSize;
    }
    

    调用header_size()方法获取ConstMethod本身占用的内存大小,然后加上extra_words就是需要开辟的内存大小,单位为字。

    2、为Method设置属性

    调用Method::allocate()方法分配Method对象与ConstMethod对象后,会在parse_method()方法中设置相关的属性,代码如下:

    // Fill in information from fixed part (access_flags already set)
    m->set_constants(_cp);   
    m->set_name_index(name_index);
    m->set_signature_index(signature_index);
    
    if (args_size >= 0) {
      m->set_size_of_parameters(args_size);
    } else {
      m->compute_size_of_parameters(THREAD);
    }
    
    // Fill in code attribute information
    m->set_max_stack(max_stack);
    m->set_max_locals(max_locals);
    
    // Copy byte codes
    m->set_code(code_start);

    调用m->set_code()方法的实现如下:

    void  set_code(address code) {
        return constMethod()->set_code(code);
    }
    
    void set_code(address code) {
        if (code_size() > 0) {
           memcpy(code_base(), code, code_size());
        }
    }
    address code_base() const {
        return (address) (this+1); // 存储在ConstMethod本身占用的内存之后
    }
    

    当字节码的大小不为0时,调用memcpy()方法将字节码内容拷贝到紧挨着ConstMethod本身占用的内存之后。

    另外还会填充ConstMethod中的相关属性,因为之前已经开辟好了存储空间,所以根据解析的结果得到相关的值后也会做填充,这一部分繁琐但没有特别的逻辑,这里不介绍。 

    相关文章的链接如下:

    1、在Ubuntu 16.04上编译OpenJDK8的源代码 

    2、调试HotSpot源代码

    3、HotSpot项目结构 

    4、HotSpot的启动过程 

    5、HotSpot二分模型(1)

    6、HotSpot的类模型(2)  

    7、HotSpot的类模型(3) 

    8、HotSpot的类模型(4)

    9、HotSpot的对象模型(5)  

    10、HotSpot的对象模型(6) 

    11、操作句柄Handle(7)

    12、句柄Handle的释放(8)

    13、类加载器 

    14、类的双亲委派机制 

    15、核心类的预装载

    16、Java主类的装载  

    17、触发类的装载  

    18、类文件介绍 

    19、文件流 

    20、解析Class文件 

    21、常量池解析(1) 

    22、常量池解析(2)

    23、字段解析(1)

    24、字段解析之伪共享(2) 

    25、字段解析(3)  

    26、字段解析之OopMapBlock(4)

    27、方法解析之Method与ConstMethod介绍  

    作者持续维护的个人博客classloading.com

    关注公众号,有HotSpot源码剖析系列文章!

      

     

  • 相关阅读:
    hiho150周
    hdu1011
    hiho1055/hdu1561
    bat脚本启动exe并打开文件后退出 + 中文乱码
    hiho1080
    hiho1079
    java异常处理——基础篇
    找不到要编译的文件——path环境变量配置
    MVC——studying
    轻松搞定EasyUI
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13436632.html
Copyright © 2020-2023  润新知