>>分享Java编程技术,对《Java面向对象编程》等书籍提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 13290 个阅读者 刷新本主题
 * 贴子主题:  Java虚拟机进行类连接的原理 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2024-04-19 02:34:37     消息  查看  搜索  好友  邮件  复制  引用

在类的连接之前要保证对类进行了解析,例如初始化一个类时会调用initialize_class()方法,实现如下:
static void initialize_class(Symbol* class_name, TRAPS) {
  Klass* klass = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
  InstanceKlass::cast(klass)->initialize(CHECK);
}

在类的初始化过程中,首先要调用SystemDictionary::resolve_or_fail()方法保证类被正确装载,如果类没有被装载,那么最终会调用到ClassFileParser::parseClassFile()方法装载类,并通过创建ConstantPool、Method、InstanceKlass等对象将元数据保存到HotSpot中。然后在调用的initialize()方法中会间接调用InstanceKlass::link_class_impl()方法进行类的连接。InstanceKlass::link_class_impl()方法的实现如下:

bool InstanceKlass::link_class_impl(instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {

  // return if already verified
  //  通过_init_state属性的值来判断类是否已经验证过
  if (this_oop->is_linked()) {
    return true;
  }

  JavaThread* jt = (JavaThread*)THREAD;

  // 在连接子类之前先连接父类
  // link super class before linking this class
  instanceKlassHandle  super(THREAD, this_oop->super());
  if (super.not_null()) {
    if (super->is_interface()) {  // check if super class is an interface
      return false;
    }

    link_class_impl(super, throw_verifyerror, CHECK_false); // 递归调用此方法进行连接操作
  }

  // 连接该类实现的所有接口
  // link all interfaces implemented by this class before linking this class
  Array<Klass*>* interfaces = this_oop->local_interfaces();
  int num_interfaces = interfaces->length();
  for (int index = 0; index < num_interfaces; index++) {
    HandleMark hm(THREAD);
    instanceKlassHandle ih(THREAD, interfaces->at(index));
    link_class_impl(ih, throw_verifyerror, CHECK_false); // 递归调用此方法进行连接操作
  }

  // in case万一 the class is linked in the process of linking its superclasses
  if (this_oop->is_linked()) {
    return true;
  }


  // verification & rewriting 验证和重写
  {
    oop           init_lock = this_oop->init_lock();
    ObjectLocker  ol(init_lock, THREAD, init_lock != NULL);
    // rewritten will have been set if loader constraint error found
    // on an earlier link attempt
    // don't verify or rewrite if already rewritten

    if (!this_oop->is_linked()) {
      if (!this_oop->is_rewritten()) {
        {
          // 进行字节码验证
          bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
          if (!verify_ok) {
             return false;
          }
        }
        // Just in case a side-effect of verify linked this class already
        // (which can sometimes happen since the verifier loads classes
        // using custom class loaders, which are free to initialize things)
        // 可能有时候会在验证的过程中导致类已经被连接,不过并不会进行类的初始化
        if (this_oop->is_linked()) {
           return true;
        }
        // 重写类
        // also sets rewritten
        this_oop->rewrite_class(CHECK_false);
      } // end rewritten

      // 完成类的重写后进行方法连接
      // relocate jsrs and link methods after they are all rewritten
      this_oop->link_methods(CHECK_false);

      // Initialize the vtable and interface table after
      // methods have been rewritten since rewrite may fabricate(编造; 捏造) new Method*s.
      // also does loader constraint checking
      // 初始化vtable和itable
      if (!this_oop()->is_shared()) {
         ResourceMark rm(THREAD);
         klassVtable* kv = this_oop->vtable();
         kv->initialize_vtable(true, CHECK_false);

         klassItable* ki = this_oop->itable();
         ki->initialize_itable(true, CHECK_false);
      }

      // 将类的状态标记为已连接状态
      this_oop->set_init_state(linked);
    }// end linked

  }// end  verification & rewriting

  return true;
}

方法的逻辑非常清晰,我们可以很容易读懂类的连接过程,步骤总结如下:

连接父类和实现的接口。因为根据虚拟机规范,子类的初始化必然会导致父类的初始化,所以子类在连接之前自然要保证父类已经连接;
-进行字节码验证;
-重写类;
-连接方法;
-初始化vtable和itable;
-将类的状态标记为已连接。

大概的执行逻辑如下图所示。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

在连接类之前需要判断此类是否已经连接,之前介绍过,每个InstanceKlass中都定义了一个_init_state属性,如下:

u1  _init_state;    // state of class

值只能为ClassState枚举类中定义的枚举常量,如下:

// See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description
// of the class loading & initialization procedure, and the use of the states.
enum ClassState {
    allocated,            // allocated (but not yet linked)
    loaded,               // loaded and inserted in class hierarchy (but not linked yet)
    linked,               // successfully linked/verified (but not initialized yet)  连接包括验证、准备和解析阶段
    being_initialized,    // currently running class initializer
    fully_initialized,    // initialized (successfull final state)
    initialization_error  // error happened during initialization
};

这些状态主要标注一个类的加载、连接和初始化状态,3个过程已经在虚拟机规范中有了明确的规定,参考地址为https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html。

allocated状态表示已经分配内存,在InstanceKlass的构造函数中通常会将_init_state初始化为这个状态。

loaded状态表示类已经装载并且已经插入到继承体系中,在 SystemDictionary::add_to_hierarchy()方法中会更新InstanceKlass的_init_state状态。

linked状态表示已经成功连接/校验,只在InstanceKlass::link_class_impl()方法中更新为这个状态。

另外3个状态是在类的初始化方法InstanceKlass::initialize_impl()中会使用,后面也会介绍到。




程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->内部类
  JavaWeb开发-->使用Session(Ⅰ)
  JSP与Hibernate开发-->立即检索和延迟检索策略
  Java网络编程-->基于UDP的数据报和套接字
  精通Spring-->Vue Router路由管理器
  Vue3开发-->Vue Router路由管理器
  NIO模式的IO多路复用底层原理
  Java设计模式: 单一职责原则和依赖倒置原则详解
  Java如何遍历Enumeration
  Java中保留数字的若干位小数位数的方法
  靠一个HashMap的讲解打动了头条面试官
  编程语言搜索量排行:用十年数据告诉你什么最受欢迎
  小数在内存中是如何存储的?
  Eclipse使用指南:快速修复功能
  Eclipse使用指南:工作空间(Workspace)
  Eclipse使用指南:常用视图(View) 的用法
  正则表达式范例
  Java入门实用代码:使用 Enumeration 遍历 HashTable
  Java入门实用代码:字符串优化测试
  Java入门实用代码:字符串替换
  Java线程实现龟兔赛跑
  更多...
 IPIP: 已设置保密
树形列表:   
[url=https://dgbaccarat.or... kericnnoe 2024-04-19 02:34:37
1页 1条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。