方法解析

在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源码剖析系列文章!

  

 

原文地址:https://www.cnblogs.com/mazhimazhi/p/13436632.html