类的加载机制

       Java源文件 ——>编译器 ——>字节码文件 (.class文件)——>JVM ——>机器码,每种平台的解释器是不同的,但是实现的虚拟机是相同的,当一个程序从开始运行,这使得虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机的实例消亡,多个虚拟机实例之间数据不能共享。 

类加载的时机:

主动初始化的6种方式:

  • 创建对象实例:new 对象的时候,会对类初始化,前提是这个类么有被初始化。
  • 调用类的静态属性或为静态属性赋值。
  • 调用类的静态方法
  • 通过class文件反射创建对象
  • 初始化一个类的子类:使用子类的时候先初始化父类
  • Java虚拟机启动时被标记为启动类的类,比如main方法所在的类。

      注意:

     Java类的加载是动态的,它并不会一次性将所有的类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其它类,则在需要的时候才加载。这是为了节省内存开销。

不会进行初始化的情况:

  • 在同一个类加载器下面只能初始化一次,如果已经初始化了便不会再次初始化
  • 在编译的时候能确定下来的静态变量(编译常量),不会对类进行初始化。例如final写诗的静态变量。

 类加载的过程:(负责将字节码文件载入到内存)

 

Bootstrap ClassLoader:

       顶层的类加载器,负责加载$JAVA_HOME中的jre/linb/rt.jar 里所有的class,由C++实现,不属于ClassLoader的子类。

ExtClassLoader:

      负责加载java平台的扩展功能的jar包,包括$JAVA_HOME中的jre/lib/*.jar 或者java.ext.dirs指定目录下的jar包。

AppClassLoader:

      负责加载classpath中国指定的jar包及目录中的class。

     -- JVM加载一个类的详细过程:

     加载 ——> 链接 ——> 初始化

加载:查找并加载类的二进制数据,JVM先从磁盘中寻找该类的字节码文件,JVM从系统环境变量的 CLASSPATH里面找到字节码文件的搜索路径,  .代表当前工作目录通过类的加载器从字节码文件中把 这个类的Class对象(类的户口信息)的信息加载到JVM中。

链接:

  • 验证    文件格式,元数据,字节码,符号引用验证
  • 准备    为类的静态变量分配内存,并将其初始化为默认值,如果当前类还有基类,则继续递归的加载其 基类
  • 解析    把类中的符号引用转换为直接引用

初始化:为类的静态变量赋予正确的初始值

双亲委派模型:(要求除了顶层的启动类加载器之外,其余的类加载器都应该有自己的父类加载器)

其主要过程:

  1. 当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。
  2. 如果么有找到,就委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载的类中是否包含这个类,有就返回,么有就委托父类去加载,直到委托到启动类加载器为止。如果父类加载器为空了,就代表使用启动类加载器作为父加载器去加载该类。(就是看到String类加载器为null的时候)。
  3. 如果启动类加载加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用AppclassLoader去加载,继续失败就会抛出一个ClassNotFoundException的异常。

 其好处:

  • 安全性     避免用户自己编写的类动态的提花Java的一些核心类。如果不采用双亲委派模型的方式进行类的加载,那我们就可以随时使用自定义的类来动态代替java核心的API中定义的类。
  • 避免类的重复加载     JVM判定两个类是否是同一个类,不仅仅是根据类名是否相同进行判定的,还要判断加载该类的类加载器是否是同一个类加载器,相同的class文件被不同的类加载器加载得到的结果就是两个不同的类。
原文地址:https://www.cnblogs.com/128-cdy/p/12426088.html