请你说说Java类的加载过程

题外话(马上进入正题),关注公众号回复:学习 领取学习资料,包括电子书、蚂蚁阿里面试题、高并发mysql底层讲解视频…
在这里插入图片描述


推荐:在准备面试的同学可以看看这个系列

        面试干货1——请你说说Java类的加载过程

        面试干货2——你对Java GC垃圾回收有了解吗?

        面试干货3——基于JDK1.8的HashMap(与Hashtable的区别、数据结构、内存泄漏…)

        面试干货4——你对Java类加载器(自定义类加载器)有了解吗?

        面试干货5——请详细说说JVM内存结构(堆、栈、常量池)

        面试干货6——输入网址按下回车后发生了什么?三次握手与四次挥手原来如此简单!

        面试干货7——刁钻面试官:关于redis,你都了解什么?

        面试干货8——面试官:可以聊聊有关数据库的优化吗?

        面试干货9——synchronized的底层原理


1. 什么是类的加载过程

        一个Java文件从编码完成到最终运行,一般会经历两个阶段:编译期、运行期。编译,即通过javac命令,将Java文件转化为二进制字节码文件,即.class文件;运行,则是将.class文件交给JVM执行。而本文所说的类加载过程就是将.class文件中类的元信息加载进内存,创建Class对象并进行解析、初始化类变量等的过程
        JVM并不是一开始就会将所有的类加载到内存,而是用到某个类,才会去加载,只加载一次,后续会说到类的加载时机

2. 类加载详解

        类加载分为三个部分:加载、连接、初始化

2.1 加载

        类的加载主要的职责为将.class文件的二进制字节流读入内存(JDK1.7及之前为JVM内存,JDK1.8及之后为本地内存),并在堆内存中为之创建Class对象,作为.class进入内存后的数据的访问入口。在这里只是读入二进制字节流,后续的验证阶段就是要拿二进制字节流来验证.class文件,验证通过,才会将.class文件转为运行时数据结构
        ++拓展: 在JDK1.7及以前,Hot Spot JVM(普遍在用的JVM)存在一块叫做方法区的内存,也称之为永久代,这块区域用于存放类的元数据信息,包括类的字段,版本,方法等,这块区域,可以理解为.class文件进入内存后的位置。在JDK1.8,取消了方法区,取而代之的是元数据区,该元数据区并非JVM内存,而是本地内存。此外在JDK1.7时,将常量池从方法区移除,在堆内存开辟了一块空间作为常量池,有人说这是为取消方法区做的准备。更多请点我看思维导图总结
        ++加分项: 为何取消方法区?
        官方说法为:移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代
        现实使用中存在问题:方法区存储类的元数据信息,我们不清楚一个程序到底有多少类需要被加载,且方法区位于JVM内存,我们不清楚需要给方法区分配多大内存,太小容易PermGen OOM,太大,在触发Full GC时又极其影响性能,同时还存在一些内存泄露的问题

2.2 连接

        类的连接分为三个阶段:验证、准备、解析。
         验证: 该阶段主要是为了保证加载进来的字节流符合JVM的规范,不会对JVM有安全性问题。其中有对元数据的验证,例如检查类是否继承了被final修饰的类;还有对符号引用的验证,例如校验符号引用是否可以通过全限定名找到,或者是检查符号引用的权限(private、public)是否符合语法规定等。
        准备: 准备阶段的主要任务是为类的类变量开辟空间并赋默认值。
1、静态变量是基本类型(int、long、short、char、byte、boolean、float、double)的默认值为0
2、静态变量是引用类型的,默认值为null
3、静态常量默认值为声明时设定的值
例如:public static final int i = 3; 在准备阶段,i的值即为3
        解析: 该阶段的主要职责为将Class在常量池中的符号引用转变为直接引用,此处针对的是静态方法及属性和私有方法与属性,因为这类方法与私有方法不能被重写,静态属性在运行期也没有多态这一说,即在编译器可知,运行期不可变,所以适合在该阶段解析,譬如类方法main替换为直接引用,为静态连接,区别于运行时的动态连接(后续我会写关于JVM内存结构的文章,在讲解栈帧时会介绍动态链接)。
        符号引用即字符串,说白了可以是一个字段名,或者一个方法名;直接引用即偏移量,说白了就是类的元信息位于内存的地址串,例如,一个类的方法为test(),则符号引用即为test,这个方法存在于内存中的地址假设为0x123456,则这个地址则为直接引用。

2.3 初始化

        该阶段主要是为类的类变量初始化值的,初始化有两种方式:
1、在声明类变量时,直接给变量赋值
2、在静态初始化块为类变量赋值

3. 类的加载时机(包括加载、连接、初始化)

  1. 创建该类的实例
  2. 调用该类的类方法
  3. 访问类或接口的类变量,或为类变量赋值
  4. 利用反射Class.forName(String name, boolean initialize,ClassLoader loader);
    当使用ClassLoader类的loadClass()方法来加载类时,该类只进行加载阶段,而不会经历初始化阶段,使用Class类的静态方法forName(),根据initialize来决定会不会初始化该类,不传该参数默认强制初始化
  5. 初始化该类的子类
  6. 运行main方法,main方法所在类会被加载

类加载顺序

  1. 先加载并连接当前类
  2. 父类没有被加载,则去加载、连接、初始化父类,依旧是先加载并连接,然后再判断有无父类,如此循环,所以JVM先将Object加载
  3. 如果类中有初始化语句,包括声明时赋值与静态初始化块,则按顺序进行初始化

由此可以理解类的初始化顺序:先执行父类静态变量赋值、父类静态初始化块,再执行子类静态属性赋值、静态初始化块。

4. 总结

        在Java类加载这块,还有很多内容需要学习,例如类加载器有几种,加载机制有几种,类加载器加载.class的步骤等,这些内容都掌握了,我们才能在面试中脱颖而出。想要高薪,我们不仅要知其然,更要知其所以然
        更多知识点脑图可点击这里(思维导图地址)。后续的文章介绍了垃圾回收机制、类加载器以及自定义类加载器、HashMap底层原理及其数据结构等,希望可以在学习中帮到你,如果你是为了面试,那么祝你面试顺利
        这篇文章就到这里了,如有问题,欢迎各位大佬指正,切不可误人子弟。

  • 49
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 38
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值