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_NoClassDefFoundErrorif (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 messageTHROW_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 threadsthis_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 ExceptionInInitializerErrorJvmtiExport::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 ExceptionInInitializerErrorJvmtiExport::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 compilereturn;}// 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 argumentsJavaValue result(T_VOID);// JavaCalls::call 表示调用java 方法,这里就是调用 clinit 方法JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)}
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第三季《乐队风暴》全国总决赛圆满落幕

2024年1月21日,由广东珠江、盛娱星汇海选联合主办的第三季《乐队风暴》全国海选歌手赛道全国总决赛在广州罗格镇MUSIC LIVE(太古仓店)正式打响,第三季《乐队风暴》全国海选开启以来共有超8000人报名渴望登上绚丽舞台,从…

二叉搜索树、二叉排序树(查找、插入和删除)——Java版本

1. 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所有节点的值都大于根节点的值它的左右子树也分别…

2024年mongodb自建三节点副本集详细教程

环境说明 系统centos7.9 自建服务器或云服务器,硬件要求不低于2核2G内存,20G硬盘,文件系统默认是ext4即可。 生产环境最好单独一个磁盘存放数据库,方便数据备份和还原,避免干扰到其他磁盘的运作。 mongodb 4.4.27 …

HTML-表格

表格 1.基本结构 一个完整的表格由:表格标题、表格头部、表格主体、表格脚注,四部分组成 表格涉及到的标签: table:表格 caption:标题 thead:表格头部 tbody:表格主体 tfoot:表格注…

android:persistent和android:priority的区别,对进程优先级有什么影响?

前言:写的apk因为系统busy给我kill了,(adj 900): kill all background,在AndroidManifest.xml添加android:persistent"true"后,被甲方要求不能这样做,还是得从adj改,把 priority改成1000 android…

可Pin to Pin兼容DRV8837的国产H桥电机驱动芯片,具大电流,短gnd,短电源保护功能

在国产牙刷,电子锁设计中,以前方案很多采用TI的DRV8837做直流电机驱动,随着中美贸易战和牙刷,电子锁等产品价格平民化普及,很多大厂在做国产化替代设计方案,GLOBALCHIP 的电机驱动芯片GC8837,价…

解读Android进程优先级ADJ算法

本文基于原生Android 9.0源码来解读进程优先级原理,基于篇幅考虑会精炼部分代码 一、概述 1.1 进程 Android框架对进程创建与管理进行了封装,对于APP开发者只需知道Android四大组件的使用。当Activity, Service, ContentProvider, BroadcastReceiver任一组件启动时,当其所…

YOLOv8改进 | Conv篇 | 2024.1月最新成果可变形卷积DCNv4(适用检测、Seg、分类、Pose、OBB)

一、本文介绍 本文给大家带来的改进机制是2024-1月的最新成果DCNv4,其是DCNv3的升级版本,效果可以说是在目前的卷积中名列前茅了,同时该卷积具有轻量化的效果!一个DCNv4参数量下降越15Wparameters左右,。它主要通过两个方面对前一版本DCNv3进行改进:首先,它移除了空间聚…

探讨Go语言中的HTTP代理模式:看Go如何玩转网络中转站

在互联网的海洋中,HTTP代理服务器像一座灯塔,为我们的网络冲浪提供了指引。而当Go语言遇上HTTP代理,会碰撞出怎样的火花呢?今天,让我们一起探讨Go语言中的HTTP代理模式,看看它如何玩转这个网络中转站&#…

IDEA(十)2022版本 Services中服务窗口不显示端口号解决

目录 一、问题描述二、问题分析三、解决方案3.1 设置启动参数【生效】3.2 方法二:设置环境变量【不生效】3.3 方法三:删除缓存【不生效】 四、补充:如何手动控制端口显示 一、问题描述 我们在使用 IDEA 的过程中,会发现在 Servic…

dpdk网络转发环境的搭建

文章目录 前言ip命令的使用配置dpdk-basicfwd需要的网络结构测试dpdk-basicfwddpdk-basicfwd代码分析附录basicfwd在tcp转发时的失败抓包信息DPDK的相关设置 前言 上手dpdk有两难。其一为环境搭建。被绑定之后的网卡没有IP,我如何给它发送数据呢?当然&a…

[leetcode] 18. 四数之和

文章目录 题目描述解题方法排序 双指针java代码 相似题目 题目描述 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应&a…

二、Kotlin 内置类型

1. 基本类型 1.1 Kotlin 和 Java 的基本类型对比 KotlinJava字节Bytebyte/Byte整型Int & Longint/Integer & long/Long浮点型Float & Doublefloat/Float & double/Double字符Charchar/Chararcter字符串StringString 1.2 定义变量 1.2.1 val 只读变量 &…

qt初入门5:字体设置和元对象系统的练习

空闲时间,参考课本demo,做一下练习。 字体的颜色主要用QPalette类,调色板的作用,控制着窗口部件的颜色和外观,包括背景色、前景色、文本颜色、边框颜色等。 字体的显示样式主要用Font类,用于管理字体。 元…

无限学模式-“重塑科研学习路径:ChatGPT应用实战课,开启高效率、高创新的科研之旅!“

ChatGPT 在论文写作与编程方面也具备强大的能力。无论是进行代码生成、错误调试还是解决编程难题,ChatGPT都能为您提供实用且高质量的建议和指导,提高编程效率和准确性。此外,ChatGPT是一位出色的合作伙伴,可以为您提供论文写作的…

【研0日记】24.01.25

回家倒数第6天 受不了了,不想写了,这群b怎么这么能写 用latex写了个伪代码,有点好玩 \usepackage[ruled,linesnumbered]{algorithm2e} \begin{algorithm}[ht] \caption{Pipeline of Kernel Iteration in K-Net.} \label{alg:alg1} …

在Java中如何优雅使用正则表达式?

在Java中如何优雅使用正则表达式? 一、正则表达式的基本概念与用途 1.1 正则表达式的简介 正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算…

深入理解badblocks

文章目录 一、概述二、安装2.1、源码编译安装2.2、命令行安装2.3、安装确认 三、重要参数详解3.1、查询支持的参数3.2、参数说明 四、实例4.1、全面扫描4.2、破坏性写入并修复4.3、非破坏性写入测试 五、实现原理六、注意事项 团队博客: 汽车电子社区 一、概述 badblocks命令是…

代码随想录算法训练营第十六天|104.二叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

104.二叉树的最大深度 思路:这道题最开始的时候,我想的是用前序遍历的思路来做,整个过程有剪枝的过程,弄了半天没写出来,主要是剪枝没写对!最大深度是叶子节点的高度,可以使用后序遍历来做。 cl…

el-table 动态渲染多级表头;一级表头根据数据动态生成,二级表头固定

一、表格需求: 实现一个动态表头,一级表头,根据数据动态生成,二级表头固定,每列的数据不一样,难点在于数据的处理。做这种表头需要两组数据,一组数据是实现表头的,另一组数据是内容…