阿里P7带你深入理解Java虚拟机总结——类初始化过程

类的初始化过程

image.png

非法向前引用

编译器手机的顺序是由语句在源文件中出现的顺序决定的,静态语句块中只能访问到定义在静态语句之前的变量,定义它之后的变量,可以赋值,但不能访问

public class Test{
 static{
 i=0;
 system.out.print(i);//非法向前引用
 }
 static int i=1;
}

(类构造器方法):在jvm第一次加载class文件时调用,如果类或者接口没有静态语句块,也没有对变量的赋值,那么编译器可以不为这个类生成方法并且他被加锁了,既不能做耗时操作。 Tips:如果在此方法中耗时很长,就可能造成多个进程阻塞;

类加载的时机

加载

加载与连接阶段的验证动作是交叉进行的

连接

验证

  • 文件格式验证。是否符合Class文件格式的规范
  • 语义分析。父类,抽象类,接口等。
  • 字节码验证
  • 符号引用验证

准备

正式为类变量分配内存并设置类变量初始值的阶段

public static int value=123;
//final的话 准备阶段既123;
//非常量的 static 则准备阶段是0;<clinit>类构造方法执行才会变成123

解析

可选的,loadClass第二个参数来判定是否需要解释。这里的解释是根据勒种的符号引用查找相应的实体,在把符号引用替换成一个直接引用的过程。

初始化

使用

卸载

类什么时候才被初始化

只有这6中情况才会导致类的类的初始化

  • 创建类的实例,也就是new一个对象
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(Class.forName("com.lyj.load"))
  • 初始化一个类的子类(会首先初始化子类的父类)
  • JVM启动时标明的启动类,即文件名和类名相同的那个类
    所有引用类的方法都不会触发初始化,称为被动引用。

类引用父类的静态字段,不会导致该类被初始化

类的初始化步骤:

  • 如果这个类还没有被加载和链接,那先进行加载和链接
  • 假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
  • 加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。

双亲委派模型
Java虚拟器角度仅仅有两种不同的类加载器:

一种启动类加载器(Bootstrap ClassLoader):C++语言实现是虚拟器自身的一部分;

另一种是所有其他的类加载器(java语言,JVM之外 继承ClassLoader)

更详细:


image.png

Bootstrap ClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class既核心API(ExtClassLoader和AppClassLoader也在此时被加载),由C++实现,不是ClassLoader子类

Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

App ClassLoader:负责记载classpath中指定的jar包及目录中class.可以通过getSystemClassLoader()方法获得

Custom ClassLoader:属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

为什么这么设计?
类加载器:任何一个类都需要加载它的类加载器和这个类一同确立其在java虚拟机唯一性。每个类加载器都有类名称空间。

两个类是否相同,是由同一个类加载器为前提下才有意义.相同是指equals、instanceof isAssignalbeFrom isIntance等;

例如类java.lang.Object,他存放在rt.jar中,无论哪个类加载器加载这个类,最终都是委派给魔性最顶端的启动类加载器进行加载。因此Object类在程序的各种类加载器环境中都是同一个类。 相反如果没有使用,各个类加载自行加载的话。那么系统将出现多个不同的Object类,那么java类型体系中最基本的行为也无法保证;

以下是ClassLoader的源码,实现很简单

rotected synchronized Class<?> loadClass(String name, boolean resolve)
 throws ClassNotFoundException
 {
 // First, check if the class has already been loaded
 Class c = findLoadedClass(name);
 if (c == null) {
 try {
 if (parent != null) {
 //从父加载器加载
 c = parent.loadClass(name, false);
 } else {
 //从bootstrap loader加载
 c = findBootstrapClassOrNull(name);
 }
 } catch (ClassNotFoundException e) {
 // ClassNotFoundException thrown if class not found
 // from the non-null parent class loader
 }
 if (c == null) {
 // If still not found, then invoke findClass in order
 // to find the class.
 c = findClass(name);
 }
 }
 if (resolve) {
 resolveClass(c);
 }
 return c;
 }

其他思考的问题

类加载的逻辑应该在哪里写?

jdk 1.2之后 已经不提倡用户覆盖loadClass方法了,而是应当在自己的类加载逻辑写到findClass()中。因为loadClass()方法如果加载失败就会调用自己的findClass()去加载。这样就能保证 写出来的类加载器是符合双亲委派模式的

如果依赖特定的扩展包,需要继承特定的classLoader吗?

下面的默认构造器, 要在父亲是AppClassLoader的基础上 加载自己的类。不然会破坏双亲的 总结:新的ClassLoader需要旧的去委托。如果不这样就会导致在同一个类出现在不同的ClassLoader中。

protected ClassLoader() {
//getSystemClassLoader()其实就是AppClassLoader
this(checkCreateClassLoader(), getSystemClassLoader());
}

如果类已经 加载过了,那么应该在哪里存储 下一次去验证是否加载过呢?

可以通过该ClassLoader中 protect findLoadedClass(name)方法找到。

resolveClass 什么时候使用

类加载过程总结

  • 得到类的原始字节数组byte[]。
  • findClass里通过defineClass(name, buf, 0, buf.length) 完成类加载。

举例分析流程
如果一个非ClassPath目录下的新的数据流类通过新的ClassLoader(NewClassLoader)去加载。

Parent:

NewClassLoader-AppClassLoader->ExtClassLoader

第一次初始化

loadClass:每次findLoadedClass都找不到
NewClassLoader-AppClassLoader->ExtClassLoader

findClass:(loadClass的逆序)

ExtClassLoader->AppClassLoader->NewClassLoader(最后在此define 生成类后loadClass一次)

第二次初始化

loadClass:第一个NewClassLoader中findLoadedClass就找到了

NB技巧:子类可以公开父类中的protected的方法;

public void findClass_(){
 super.findClass();//protected
}

欢迎大家加入粉丝群:963944895,群内免费分享Spring框架、Mybatis框架SpringBoot框架、SpringMVC框架、SpringCloud微服务、Dubbo框架、Redis缓存、RabbitMq消息、JVM调优、Tomcat容器、MySQL数据库教学视频及架构学习思维导图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容