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

Rsync服务

一、Rsync概述 rsync英文称为 remote synchronizetion,rsync具有可使本地和远程两台主机之间的数据快速复制同步镜像、远程备份的功能,功能类似于ssh带的scp命令,优于scp命令的功能,scp每次都是全量拷贝,而rsync可以增…

Rust Web小项目

Rust 第26节 Web小项目 监听TCP链接 use std::net::TcpListener;fn main() {let listener TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerfor stream in listener.incoming() { // listener.…

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进行改进:首先,它移除了空间聚…

Python 流静态文件过滤、端口过滤、同域过滤(host过滤)、代理拦截

目录 静态文件过滤 需求 代码 端口过滤 需求 代码 同域过滤(host过滤) 需求 代码 静态文件过滤 需求 流量中的url包含大量静态文件请求信息,过滤掉 代码 def __is_static(self, flow: http.HTTPFlow) -> bool:static_ext [.j…

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

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

三:C语言-输入与输出

三:输入与输出 一:输出 1.printf(): ​ 将参数文本输出到屏幕上,它名字里的 f 代表 format(格式化),表示可以定制输出文本的格式 ​ printf()不会在行尾自动添加换行符,待运行结…

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

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

Hive之set参数大全-11

设置 Map Join 操作中优化哈希表的工作集大小(working set size) hive.mapjoin.optimized.hashtable.wbsize 是 Apache Hive 中的一个配置属性,用于设置 Map Join 操作中优化哈希表的工作集大小(working set size)。 …

Dockerfile:如何写一个Dockerfile文件?

如何写一个Dockerfile文件? 🚨推荐参考:Dockerfile:如何写一个Dockerfile文件? 现在的项目肯定都离不开docker,只要是流水线部署就会涉及Dockerfile文件,那么如何写一个正确的编写一个Dockerfil…

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类,用于管理字体。 元…