Hotspot源码解析-第25章-类的初始化

第25章-类的初始化

这一章主要是讲类的初始化操作,后续类加载章节中也会用到这一章的知识,只不过,这里就讲,是因为虚拟在初始化过程中,需要对基础类,比如System/Thread等类进行初始化操作,所以就提前把这块内容先讲掉。

调用链

thread.cpp->create_vm()

// 这里只摘取了部分代码
initialize_class(vmSymbols::java_lang_String(), CHECK_0);

// Initialize java_lang.System (needed before creating the thread)
initialize_class(vmSymbols::java_lang_System(), CHECK_0);
initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0);

thread.cpp->initialize_class(Symbol* class_name, TRAPS)

static void initialize_class(Symbol* class_name, TRAPS) {
  Klass* klass = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
  InstanceKlass::cast(klass)->initialize(CHECK);
}

instanceKlass.cpp->InstanceKlass::initialize

void InstanceKlass::initialize(TRAPS) {
  if (this->should_be_initialized()) {
    HandleMark hm(THREAD);
    instanceKlassHandle this_oop(THREAD, this);
    initialize_impl(this_oop, CHECK); // 真正的初始化操作在这里,看`章节25.1`
    // Note: at this point the class may be initialized
    //       OR it may be in the state of being initialized
    //       in case of recursive initialization!
  } else {
    assert(is_initialized(), "sanity check");
  }
}

25.1 类的初始化

25.1.1 instanceKlass.cpp

25.1.1.1 initialize_impl

函数中总共分成11个步骤来完成,这11个步骤在Java虚拟机规范文档中有描述,下面我们看看初始化时到底做了啥

void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
  // Make sure klass is linked (verified) before initialization
  // A class could already be verified, since it has been reflected upon.
  this_oop->link_class(CHECK);

  DTRACE_CLASSINIT_PROBE(required, InstanceKlass::cast(this_oop()), -1);

  bool wait = false;

  // refer to the JVM book page 47 for description of steps
  // Step 1
  {
      // 步骤1,初始化之前通过 ObjectLocker 对初始化操作加锁,防止多线程同步问题
    oop init_lock = this_oop->init_lock();
    ObjectLocker ol(init_lock, THREAD, init_lock != NULL);

    Thread *self = THREAD; // it's passed the current thread

    // Step 2
    // 步骤2,正在初始化的线程不是当前线程,那么释放获取到的锁,同时当前线程进入阻塞状态,等待其他线程完成初始化操作
    while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) {
        wait = true;
      ol.waitUninterruptibly(CHECK);
    }

    // Step 3
    // 步骤3,正在初始化的线程就是当前线程,那就表明这是对初始化的递归请求,释放锁
    if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) {
      DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }

    // Step 4
    // 步骤4,已经初始化完成,那就不做什么,直接释放锁并返回
    if (this_oop->is_initialized()) {
      DTRACE_CLASSINIT_PROBE_WAIT(concurrent, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }

    // Step 5
    // 步骤5,初始化过程出现错误,那就直接抛出异常 java_lang_NoClassDefFoundError
    if (this_oop->is_in_error_state()) {
      DTRACE_CLASSINIT_PROBE_WAIT(erroneous, InstanceKlass::cast(this_oop()), -1,wait);
      ResourceMark rm(THREAD);
      const char* desc = "Could not initialize class ";
      const char* className = this_oop->external_name();
      size_t msglen = strlen(desc) + strlen(className) + 1;
      char* message = NEW_RESOURCE_ARRAY(char, msglen);
      if (NULL == message) {
        // Out of memory: can't create detailed error message
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
      } else {
        jio_snprintf(message, msglen, "%s%s", desc, className);
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
      }
    }

    // Step 6
    // 步骤6,设置初始化状态为being_initialized,设置初始化的线程为当前线程
    this_oop->set_init_state(being_initialized);
    this_oop->set_init_thread(self);
  }

  // Step 7
  // 步骤7,如果当前初始化的类是类而不是接口,并且该类的父类还没有初始化,那就在父类上递归进行完整的初始化过程。如果父类初始化过程抛出异常,那么该类的初始化也要标为初始化错误状态,并通知所有正在等待的线程,然后抛出父类初始化抛出的异常。
  if (!this_oop->is_interface()) {
    Klass* super_klass = this_oop->super();
    if (super_klass != NULL && super_klass->should_be_initialized()) {
      super_klass->initialize(THREAD);  // 父类初始化过程
    }

    if (!HAS_PENDING_EXCEPTION && this_oop->has_default_methods()) {
      this_oop->initialize_super_interfaces(this_oop, THREAD);
    }

    // If any exceptions, complete abruptly, throwing the same exception as above.
    if (HAS_PENDING_EXCEPTION) {
      Handle e(THREAD, PENDING_EXCEPTION);
      CLEAR_PENDING_EXCEPTION;
      {
        EXCEPTION_MARK;
        // Locks object, set state, and notify all waiting threads
        this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
        CLEAR_PENDING_EXCEPTION;
      }
      DTRACE_CLASSINIT_PROBE_WAIT(super__failed, InstanceKlass::cast(this_oop()), -1,wait);
      THROW_OOP(e());
    }
  }

  // Step 8
  // 步骤8,做一些检查工作及性能数据处理后,就真正调用初始化函数,进行实际初始化工作
  {
    assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
    JavaThread* jt = (JavaThread*)THREAD;
    DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait);
    // Timer includes any side effects of class initialization (resolution,
    // etc), but not recursive entry into call_class_initializer().
    PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),
                             ClassLoader::perf_class_init_selftime(),
                             ClassLoader::perf_classes_inited(),
                             jt->get_thread_stat()->perf_recursion_counts_addr(),
                             jt->get_thread_stat()->perf_timers_addr(),
                             PerfClassTraceTime::CLASS_CLINIT);
    this_oop->call_class_initializer(THREAD);  // 调用类和接口的初始化函数,看`章节25.1.1.2`
  }

  // Step 9
  // 步骤9,初始化过程没有任务异常,说明已经完成初始化,设置类的状态为full_initialized并通知其他线程初始化已完成
  if (!HAS_PENDING_EXCEPTION) {
    this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
    { ResourceMark rm(THREAD);
      debug_only(this_oop->vtable()->verify(tty, true);)
    }
  }
  else {
    // Step 10 and 11
    // 步骤10、11,初始化过中发生异常,则通过 set_initialization_state_and_notify 函数设置类的状态 initialization_error ,同时通知其他线程,并抛出异常
    Handle e(THREAD, PENDING_EXCEPTION);
    CLEAR_PENDING_EXCEPTION;
    // JVMTI has already reported the pending exception
    // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
    JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
    {
      EXCEPTION_MARK;
      this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
      CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
      // JVMTI has already reported the pending exception
      // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
      JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
    }
    DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait);
    if (e->is_a(SystemDictionary::Error_klass())) {
      THROW_OOP(e());
    } else {
      JavaCallArguments args(e);
      THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                vmSymbols::throwable_void_signature(),
                &args);
    }
  }
  DTRACE_CLASSINIT_PROBE_WAIT(end, InstanceKlass::cast(this_oop()), -1,wait);
  // 至此,类的初始化工作完成。
}
25.1.1.2 call_class_initializer
void InstanceKlass::call_class_initializer(TRAPS) {
  instanceKlassHandle ik (THREAD, this);
  call_class_initializer_impl(ik, THREAD);
}

// 从_methods数组中查询出 clinit 函数,并返回,_methods 数组内容来源于类加载和解析,这块内容会在类加载一章中讲,现在只要知道有这么一个数组存放该 klass/对象 的所有方法就行。这里也简单介绍下clinit,该方法是由编译器自动生成的,看`图25-1`
Method* InstanceKlass::class_initializer() {
  Method* clinit = find_method(
      vmSymbols::class_initializer_name(), vmSymbols::void_method_signature());
  if (clinit != NULL && clinit->has_valid_initializer_flags()) {
    return clinit;
  }
  return NULL;
}

void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) {
  if (ReplayCompiles &&
      (ReplaySuppressInitializers == 1 ||
       ReplaySuppressInitializers >= 2 && this_oop->class_loader() != NULL)) {
    // Hide the existence of the initializer for the purpose of replaying the compile
    return;
  }
  // clinit 方法的句柄
  methodHandle h_method(THREAD, this_oop->class_initializer());
  assert(!this_oop->is_initialized(), "we cannot initialize twice");
  if (TraceClassInitialization) {
    tty->print("%d Initializing ", call_class_initializer_impl_counter++);
    this_oop->name()->print_value();
    tty->print_cr("%s (" INTPTR_FORMAT ")", h_method() == NULL ? "(no method)" : "", (address)this_oop());
  }
  if (h_method() != NULL) {
    JavaCallArguments args; // No arguments
    JavaValue result(T_VOID);
    // JavaCalls::call 表示调用java 方法,这里就是调用 clinit 方法
    JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
  }
}

图25-1 红色框框起来的就是编译器自动生成的方法
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355308.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DL/T645、IEC104转OPC UA网关BE112

随着电力系统信息化建设和数字化转型的进程不断加速,对电力能源的智能化需求也日趋增强。健全稳定的智慧电力系统能够为工业生产、基础设施建设以及国防建设提供稳定的能源支持。在此背景下,高性能的工业电力数据传输解决方案——协议转换网关应运而生&a…

贪吃蛇项目(基于C语言和数据结构中的链表)

建立文件 首先先建立3个文件。 Snake.h 函数的声明 Snake.c 函数的定义 Test.c 贪吃蛇的测试 分析项目 我们分析这整个项目 建立节点 首先在我们实现游戏开始的部分之前,我们要先创建贪吃蛇的节点,再由此创建整个贪吃蛇所包含的一些信息&#…

Docker 基础篇

目录 一、Docker 简介 1. Docker 2. Linux 容器 3. 传统虚拟机和容器的对比 4. Docker 的作用 5. Docker 的基本组成(Docker 三要素) 6. Docker 工作原理 7. Docker 架构 8. Docker 下载 二、Docker 安装 1. CentOS Docker 安装 2. CentOS8 …

内网安全:NTLM-Relay

目录 NTLM认证过程以及攻击面 NTLM Relay攻击 NTLM攻击总结 实验环境说明 域横向移动:NTLM中继攻击 攻击条件 实战一:NTLM中继攻击-CS转发上线MSF 原理示意图 一. CS代理转发 二. MSF架设路由 三. 适用smb_relay模块进行中继攻击 域横向移动…

《HTML 简易速速上手小册》第2章:HTML 的标签和元素(2024 最新版)

文章目录 2.1 文本格式化标签(🎩✨📜 网页的“时尚搭配师”)2.1.1 基础示例:一篇博客的格式化2.1.2 案例扩展一:产品介绍页面2.1.3 案例扩展二:个人简历 2.2 链接和锚点(&#x1f6a…

案例分析技巧-软件工程

一、考试情况 需求分析(※※※※)面向对象设计(※※) 二、结构化需求分析 数据流图 数据流图的平衡原则 数据流图的答题技巧 利用数据平衡原则,比如顶层图的输入输出应与0层图一致补充实体 人物角色:客户、…

初识K8S(Kubernetes )

一、概述 Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。(官网) Kuberne…

Navicat连接Oracle时报错ORA-28547:完美解决

先用你的IDEA或者别人的连接到oracle数据库(为了查询版本) 查询版本SQL:select * from v$version; 引入对应的oci.dll文件 链接:https://pan.baidu.com/s/1volkj328Ttm-Mt0Grt1X4g 提取码:3d5f 进行下载 在Navicat配…

类和对象 第五部分第三小节:递增运算符重载

作用&#xff1a;通过重载递增运算符&#xff0c;实现自己的整型数据递增 代码案例 1.重载前置运算符 #include <iostream> #include <string> using namespace std;class MyInteger {friend ostream& operator<<(ostream& out, const MyInteger&…

【详解】贪吃蛇游戏----下篇(完整源码)

目录 引入&#xff1a; 本片文章目的&#xff1a; 整个游戏的实现流程图如下&#xff1a; 游戏实现 GameRun PrintHelpInfo Pause NextIsFood printSnake EatFood NoFood KillByWall KillBySelf GameRun GameEnd 总代码&#xff1a; &#xff08;1&#xff09…

Nestjs 全局拦截器

一、拦截器 拦截器作用&#xff1a; 在函数执行之前、之后绑定额外的逻辑转换函数的返回结果转换从函数抛出的异常扩展基本函数的行为根据所选条件重写函数 期望接口返回一个标准的json格式&#xff0c;利用拦截器对数据做全局的格式化 {code: "200",data: [],mess…

Go map 读写性能优化 - 分片 map

基本在所有的编程语言中&#xff0c;都有 map 这种数据结构&#xff0c;Go 语言也不例外。 我们知道 Go 是一门对并发支持得比较好的语言&#xff0c;但是 map 并不支持并发读写。 比如&#xff0c;下面这种写法是错误的&#xff1a; var m make(map[int]int) var wg sync.Wa…

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…

通俗易懂理解MobileNet网络模型

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 详细且通俗讲解轻量级神经网络——MobileNets【V1、V2、V3】 MobileNet v1 和 MobileNet v2 二、MobileNet v1 原始论文&#xff1a;[1] MobileNet网络详解 【深度学习】轻量化CNN网络MobileNet系列详解 Mo…

ThreadLocal学习笔记

ThreadLocal类图 ThreadLocal/InheritableThreadLocal/ \TransmittableThreadLocal(阿里巴巴) TransmissibleThreadLocal(阿里巴巴)ThreadLocal 这是Thread类的局部变量&#xff0c;每个线程私有。 它主要用于解决多线程中的数据共享问题&#xff0c;保…

Dubbo框架注册中心-Zookeeper搭建

Dubbo 是阿里巴巴公司开源的高性能、轻量级的Java RPC框架&#xff0c;致力于提供高性能。 Dubbo官网 本篇开始dubbo的第一篇&#xff0c;注册中心 ZooKeeper 环境搭建。 环境前置&#xff1a;由于Zookeeper是基于Java环境&#xff0c;必须安装有JDK。查看命令 java -version。…

【Redis】Redis有哪些适合的场景

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Redis ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 &#xff08;1&#xff09;会话缓存&#xff08;Session Cache&#xff09; &#xff08;2&#xff09;全页缓存&#xff08;FPC…

Element-Plus如何实现表单校验和表单重置

一&#xff1a;页面布局介绍&#xff1a; 这是我刚刚用基于vue3element-plus写好的一个部门管理的页面 基本的增删改查已经写好&#xff0c;下面我只提供页面的template和style的代码&#xff1a; template <template><el-card class"box-card"><…

【Javaweb程序设计】【C00165】基于SSM的高考志愿辅助填报系统(论文+PPT)

基于SSM的高考志愿辅助填报系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的高考志愿辅助填报系统 本系统分为前台系统模块、后台管理员模块以及后台学生模块 前台系统模块&#xff1a;当游客打开系统的网址后&…

CMMI、SPCA、CSMM,三种认证的差异有哪些?

在当今的企业环境中&#xff0c;体系认证已经成为了一个重要的议题。其中&#xff0c;CMMI、SPCA和CSMM是三种广泛使用的认证&#xff0c;它们在各自领域内具有特定的目标和要求&#xff0c;今天擎标就带大家了解一下这三种认证之间的差异。 CMMI、CSMM和SPCA分别是什么 1、C…