JVM-类加载过程和类加载器

类加载过程分为:

1、加载
2、验证
3、准备
4、解析
5、初始化

1、加载

类加载阶段,jvm主要完成以下3件事:

  1. 通过一个类的全限定名类获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据访问入口。

ps:这里的加载一个还有一个比较重要的内容:类加载器。这个放在下一篇文章来写。

2、验证

验证是连接阶段的第一步,这一步的目的是为了确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会威胁虚拟机自身的安全。
文件格式验证:

  • 是否以魔数:OxCAFFBABE开头(咖啡宝贝)

3、准备

准备阶段是为类中的变量(静态变量,类变量)分配内存和设置初始值的的阶段。
JDK7中是用永久代实现的方法区,所以类变量是放在方法区的,没问题。但是在JDK8及以后,类变量就会随着class对象一起放在了java堆中了。
刚刚说的给类变量设置初始值可能有同学会混淆,这个不是给类变量赋值了,只是给设置了一个初始的值。

public static int value = 123;

上面的这段代码中value中准备阶段的初始值是0,而不是123;
要把value赋值为123的putstatic指令是在程序被编译后,存放在类构造器的()方法中,所以是在类的初始化阶段才会被执行。

但是上面提到是“通常情况”,所以也会有特殊情况,看下面的这行代码,value2变量
被final修饰了,所以它是一个常量了。在编译时javac就会将value2生成ConstantValue(常量)属性,在准备阶段就会将value2赋值为123了;

public static final int value2 = 123;

4、解析

解析阶段是jvm将常量池内的符号引用替换为直接引用的过程,

5、初始化

类的初始化是类加载过程的最后一个阶段,前面的几个阶段除了在加载阶段用户应用程序可以通过自定义的类加载器的方式拒不参与外,其余的动作都完全有jvm来主导控制。直到初始化阶段,jvm才真正的开始执行类中编写的java程序代码,将主导权交给应用程序。

对于类的初始化阶段,简单来说就是:执行 类构造器< clinit>() 方法的过程。

  • < clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和**静态初始化块(static{}块)**中的语句合并产生的。
  • < clinit>()方法与类的构造函数不同,它不需要显示的调用父类的()方法,虚拟机会保证在子类的< clinit>()方法执行之前,父类的< clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的< clinit>()方法的类肯定是java.lang.Object
  • < clinit>()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成< clinit>()方法。
  • 接口中不能使用静态初始化块,但是仍有static变量的赋值操作,所以也会有< clinit>()方法,但是接口执行< clinit>()方法不需要先执行父接口的< clinit>()方法。只有当父接口中定义的变量被使用到时,才会执行< clinit>()方法
  • jvm会保证一个类的< clinit>()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的< clinit>()方法,其它线程都需要阻塞等待。(cl锁)

ps:触发初始化的指令有:

1.main方法所在类优先初始化
2.首次访问这个类的静态变量或静态方法时
3.子类初始化,如果父类还没有,会先触发父类的初始化
4.子类访问父类的静态变量,只会触发父类的父类的初始化
5.Class.forName
6.创建类的实例(new对象)

类加载器

  • 启动类加载器(bootstrap class loader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
  • 扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现。
  • 应用程序类加载器(application class loader):它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader

类加载器用作于类的的加载动作。对于任意一个类,只要它被类加载器加载了就不会被再次加载(只会加载一个在jvm中)。所以为了实现这个唯一性,就有了双亲委派模型。

**双亲委派模型的工作过程:**如果一个类加载器在接受到一个类加载请求时,它首先不会自己去尝试加载,而是会先把这个加载请求委派给它的父类加载器去先请求加载,每个层次的类加载器都会如此去做,所以最后的类加载请求都会委派到顶层类加载器bootstrap classloader,只有在父类加载器反馈无法加载该请求时,子类加载器才会自己去加载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值