【温故而知新】Java:类加载过程、类加载时机、类加载器、类加载机制理解

一、类加载过程

加载 链接  
  验证  
  准备  
  解析 初始化

1.加载

加载是指,将类的class文件,读入到内存,并为其创建java.lang.Class文件

  • 本地文件系统加载class文件——绝大部分的示例代码
  • jar包中加载——例如jdbc
  • 网络加载
  • java文件的动态加载

类加载器无须“首次使用”加载,jvm虚拟机允许系统预先加载某些类

 

2.链接

system为之生成class对象之后,会进入链接阶段,负责吧类的二进制数据合并到jre当中

 

(1)验证:

Java较C++相对安全,存在类校验,c++则没有。验证是Java的一个重要防护,防止应用被恶意入侵。越严谨的验证就越安全,例如数组越界。——保证class文件包含的信息符合jvm要求,保护jvm。

  • 文件格式验证:字节流文件符合class规范。
主次版本在jvm的范围
常量池类型
指向常量的索引值是否存在不存在的类型
  • 元数据:字节码语义分析是否符合Java语法规范
  • 字节码校验(重要):数据流和控制,确定语义合法,符合逻辑,然后才会run类的方法,不会有危险
  • 符号引用的验证:保证引用一定会被访问,解决类无法访问

 

(2)准备:

  • 为类的静态变量分配内存
  • 设置默认初始值

 

(3)解析:

  • 将二进制的符号引用替换成直接引用
  • 符号引用
  • 直接引用包括:
    目标指针、偏移量
    句柄

     

3.初始化

初始化为静态变量赋正确的默认值,这里与准备阶段区分。

例如:private static int a=5

其实是在内存空间开辟之后,准备阶段设置int默认值为0  在初始化阶段,才会赋值5

 

 

二、类加载的时机

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

 

三、类加载器:

将所有被载入内存的对象生成一个java.lang.Class实例,保证只被加载一次,且保证唯一性。

 

1.根加载器(bootstrap class loader):

加载Java的核心类,由原生代码实现,开发者无法变更引用。

 

2.扩展类加载器(extensions class loader)

加载jre扩展目录   lib/ex 或  java.ext.dirs

java实现,父类加载器是null

 

3.系统加载器(system class loader)

系统类加载器,也叫应用加载器

  • class path
  • java.class.path
  • classPath指定的jar包路劲

 

ClassLoader的静态方法  getSystemClassLoader()

  • 1.检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
  • 2.如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
  • 3.请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
  • 4.请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
  • 5.当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
  • 6.从文件中载入Class,成功后跳至第8步。
  • 7.抛出ClassNotFountException异常。
  • 8.返回对应的java.lang.Class对象。
     

 

四、类加载机制

 

1.三种类加载机制

  • 1.全盘负责:当一个类加载class时,该class的依赖和引用也由该类加载器完成
  • 2.双亲委派:先让父类加载器尝试加载,如果没有加载,再用自己的加载器加载
  • 3.缓存机制:将所有加载过的class缓存,先取缓存,如果拿不到,再讲二进制文件读入内存。所以,修改了class,需要重启才能生效。

 

2.双亲委派(重点)

双亲委派机制,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
 

 

双亲委派总结:

子类加载器很懒,并不会优先加载子类,而是交给父类加载器加载。父亲做不了,才轮到儿子。

这样做的优点是,保证优先级,只被加载一次,既提高了效率,也更加安全。举例:java.lang.Integer由父类加载器加载,防止核心API库被随意篡改。

 

你可能感兴趣的:(Java,面试必问,java,jvm)