如果面试官让你分析类初始化阶段的死锁现象

简介: 哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。

哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。

手撸过JVM、内存池、垃圾回收算法、synchronized、线程池、NIO、三色标记算法…

image.png

准备写两篇文章透彻剖析下类的初始化阶段及初始化阶段的死锁问题:

  1. 类的初始化做什么
  2. JVM底层是如何实现类的初始化的
  3. 为什么会出现死锁问题
  4. 怎么解释死锁问题
  5. 如果证明你对死锁的判断是正确的
  6. 我是如何论证的(改Hotspot源码打日志)

会由浅入深,循序渐进展开。今天是第一篇,难度偏认知层面。读起来应该会很轻松愉快。

clinit

类初始化阶段做什么?其实很简单,执行clinit方法。这个方法哪里来的?你的Java代码中只要有静态属性或者是静态代码段,在编译的时候就会自动生成这个方法。

代码中有静态属性,如图

image.png

代码中有静态方法,如图

image.png

这段代码有两个地方需要注意下:1、静态代码块前可以写static,也可以不写;2、如果代码中有多个静态代码块,编译系统会合并成一个,合并后的代码顺序跟静态代码块的先后顺序保存一致。

死锁现象

国际惯例,上代码

image.png

这段代码运行起来的结果,如图

image.png

代码永远不会结束,其实在JVM层面,是发生了死锁。

检测死锁

一提到死锁,我们会马上想起JVM提供的工具:jconsole、visualvm…期待的结果是

image.png

但是很遗憾,目前没有工具能够检测出初始化阶段发生的死锁问题(我准备尝试写一个)。

为什么jconsole检测不到呢?看图

image.png

线程根据执行代码进入的空间来分,有这四种基本状态,目前能看得到的工具,或者是JVM开放的API,只能做到检测出thread_in_Java这种状态的线程死锁问题。

所以这个问题只能通过阅读Hotspot源码找答案。所以可以推测出面试官问你这个问题,就是看你有没有这个能力,或者是这个习惯,通过阅读Hotspot源码找答案。

初始化流程

类的初始化阶段,对应Hotspot源码:InstanceKlass::initialize_impl。考虑到很多小伙伴不想看C++代码,我用伪代码把意思表达到

讲两个预备知识:

  1. 看这段代码的时候要区别正在执行初始化的线程及其他线程。假设正在执行初始化的线程为T1,又进来一个线程T2执行初始化
  2. Hotspot源码中此处有两个状态对理解代码至关重要:being_initialized(正在执行初始化)、fully_initialized(完成初始化)

image.png

问题的答案已经很清晰了:

  1. 第一个线程执行new指令,触发加载类A,然后执行A的初始化方法clinit,clinit方法中又执行到new指令,触发加载类B,并执行类B的初始化方法clinit
  2. 第二个线程触发加载类B,在类B的clinit方法中又触发加载类A
  3. 死锁的原因就是线程一跟线程二都进入了wait,也就是初始化流程的Step 2
  4. 其实这个问题存在时间差,如果某个线程跑得足够快,完成了初始化,死锁就不会发生。所以如果你的程序出现有时候卡着不动,有时候又是正常的,不妨大胆猜测有可能是发生了初始化阶段死锁。正常来讲,出现死锁的频率更高,我测试了很多次,理想情况发生的次数还是很少很少的

然,目前得出的答案是从理论层面分析出来的,那事实是不是如此呢?如何证明呢?下篇文章分享

我是子牙老师,喜欢钻研底层,深入研究Windows、Linux内核、JVM。如果你也喜欢研究底层,欢迎关注我的公众号【硬核子牙】

相关文章
|
6月前
|
Java
【面试题精讲】Throwable 类常用方法有哪些?
【面试题精讲】Throwable 类常用方法有哪些?
|
6月前
|
Java
【面试题精讲】Object类的常见方法有哪些?
【面试题精讲】Object类的常见方法有哪些?
|
4月前
面试官:除了继承Thread类和实现Runnable接口,你知道使用Callable接口的方式来创建线程吗?
面试官:除了继承Thread类和实现Runnable接口,你知道使用Callable接口的方式来创建线程吗?
18 0
面试官:除了继承Thread类和实现Runnable接口,你知道使用Callable接口的方式来创建线程吗?
|
2月前
|
存储 编译器 程序员
近4w字吐血整理!只要你认真看完【C++编程核心知识】分分钟吊打面试官(包含:内存、函数、引用、类与对象、文件操作)
近4w字吐血整理!只要你认真看完【C++编程核心知识】分分钟吊打面试官(包含:内存、函数、引用、类与对象、文件操作)
107 0
|
6月前
|
存储 安全 Java
每日一道面试题之set有哪些实现类?
每日一道面试题之set有哪些实现类?
|
6月前
|
存储 数据库
每日一道面试题之介绍一下常见的异常类有哪些?
每日一道面试题之介绍一下常见的异常类有哪些?
|
6月前
|
Java
每天一道面试题之Java中的Math类常用的方法
每天一道面试题之Java中的Math类常用的方法
|
6月前
|
Java 编译器
【面试题精讲】如果一个类没有声明构造方法,该程序能正确执行吗?
【面试题精讲】如果一个类没有声明构造方法,该程序能正确执行吗?
|
3月前
|
JSON JavaScript 前端开发
【面试题】JS判断两个数组相等的4类方法
【面试题】JS判断两个数组相等的4类方法
|
4月前
|
安全 容器
线程安全的集合类(ConcurrentHashMap面试超高频考点)
线程安全的集合类(ConcurrentHashMap面试超高频考点)
31 0
线程安全的集合类(ConcurrentHashMap面试超高频考点)

相关实验场景

更多