JVM学习之——类加载

上次讲完了Class文件的格式,那么class文件该经过什么过程才能进入到内存呢?这里就用到了类加载

加载过程 (Class Cycle)

首先讲解加载过程:

1. loading

        将class文件加载到内存中

2. linking

  • 1.Verification

        文件校验

  • 2.Preparation

        静态变量附默认值

  • 3.Resolution

        将符号引用转化为直接地址

3. Initializing

        静态变量赋值为初始值

Class Cycle

类加载器 (ClassLoader)

        类加载器也是一个普通的class文件,负责加载所有的类,并为所有加载到内存中的类生成一个生成一个java.lang.Class实例对象。已经被加载到JVM中的类具有唯一标识符,不会再被加载第二次。

        我们可以通过编码:对象名.class.getClassLoader()方法获取此对象的类加载器。
 

双亲委派机制

        JVM是按需动态加载采用双亲委派机制。注意这里“双亲”指的不是父母,而是指parent&child双向加载。

JVM预定义有三种类加载器,从下到上优先顺序为:
 
        启动类加载器 BootStrap ClassLoader

                                    ↑

        标准扩展类加载器 Extension ClassLoader

                                    ↑

        应用类加载器 Application ClassLoader
 
在实际加载过程中最底层还是由用户自定义类加载器 Custom ClassLoader开始。下图展示了每层类加载器的职责内容和加载方向。

在这里插入图片描述
类加载器选择和加载流程:
双亲委派
        通过此图可以直观明了的看出来类加载器选择和加载的过程,我简单说明一下:

        当某个.class文件要被加载时,首先会从底层类加载器开始查找是否被加载,如果有就返回,没有就向它的上一层父加载器里查找,没有就继续往上直到BootStrap ClassLoader

        若是直到BootStrap ClassLoader也没有找到,就向下进行委托加载该类,此加载器若可以加载就执行并返回,若不能加载就继续向下委托,直到自定义类加载器。

        但如果到自定义类也无法加载,就会抛出异常:ClassNotFound。通过双亲委派机制可以保证程序编译后class文件的安全性。

        这里要注意父加载器不是类加载器的加载器,也不是类加载器的父类加载器。父加载器只是加载器的一个方向说明。在编程中可以通过对象名.class.getClassLoader().getParent()方法调用。

        

类加载器范围

        java中类加载器的范围在Launcher的源码中。分别为:

  • sun.boot.class.path
    Bootstrap ClassLoader加载路径
  • java.ext.dirs
    • Extension ClassLoader加载路径
    • java.class.path
    • Application ClassLoader加载路径

通过编码:System.getPreperty("类加载器路径")查看本地类加载器的文件范围。

public class ClassLoaderPath {
    public static void main(String[] args) {
        String path;

        System.out.println("------------------------------------");
        path = System.getProperty("sun.boot.class.path");
        System.out.println(path.replaceAll(";", System.lineSeparator()));
        System.out.println("------------------------------------");


        System.out.println("------------------------------------");
        path = System.getProperty("java.ext.dirs");
        System.out.println(path.replaceAll(";", System.lineSeparator()));
        System.out.println("------------------------------------");

        path = System.getProperty("java.class.path");
        System.out.println(path.replaceAll(";", System.lineSeparator()));
    }
}

运行结果如图:

path
        

自定义类加载器

        自定义类加载器要继承ClassLoader,并重写。一般要用到findClass()defineClass(byte[] -> Class 对象名)。在要用到的地方只需调用对象.loadClass("自定义类加载器名")就可以,其返回的是Class类型对象。

什么时候需要自定义类加载器?

  1. 加密:知道了class的编译过程就知道java代码很容易被反编译,如果你需要把自己的代码进行加密,可以先将编译后的代码用某种加密算法加密,然后实现自己的类加载器,负责将这段加密后的代码还原。
  2. 从非标准的来源加载代码:例如你的部分字节码是放在数据库中甚至是网络上的,就可以自己写个类加载器,从指定的来源加载类。
  3. 动态创建、服务器热部署:为了性能等理由,根据实际情况动态创建代码并执行。

编译器

        编译器主要包括:解释器编译。Java语言在默认情况下使用的是混合模式

  • 解释器
            bytecode interpreter
    • -Xint使用纯解释模式,启动很快,执行稍慢
  • JIT
            Just-In Time compiler
    • -Xcomp使用纯编译模式,执行很快,启动较慢
  • 混合模式
            混合使用解释器和热点代码编译
    • -Xmixed默认混合模式,启动和热点代码编译都比较快

可以通过修改Run的设置来选择编译模式。
Run
Xint
Xcomp        

懒加载

Lazyloading(严格意义上应为LazyInitializing)

        JVM没有规定什么时候加载,只有被用到的时候才会加载,类似懒加载模式。但JVM严格规定了什么时候必须初始化。

                        ————————————————————————————————
补充:

父类静态代码块–>子类静态代码块–>父类普通代码块–>父类构造方法–>子类代码块–>子类构造方法;
Java程序初始化顺序:
1.父类的静态代码块
2.子类的静态代码块
3.父类的普通代码块
4.父类的构造方法
5.子类的普通代码块
6.子类的构造方法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值