[jvm]类的加载流程一
概述
定义
是采用编译型(ps 对应于 解释型),基于栈(ps 对应于 基于寄存器)实现的虚拟机
优点
适配性好,可在多种硬件上使用
java类在虚拟机中的生命周期
java程序运行时,jvm 使用一个类,主要有五步
- 加载
- 连接
- 初始化
- 使用
- 卸载
盗个图1
加载
加载定义
类的加载指的是将类的.class文件中二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个
java.lang.Class对象,用来封装类在方法区内的数据结构。在这个阶段,会执行类中声明的静态代码块。也就是类中的静态块执行时不需要等到类的初始化。2
加载类使用的对象
此处的几个对象 与Android 中的 几个对象是不同的,后续在表
- BootClassLoader
jvm启动类加载器
用c++实现为jvm的一部分(仅指sun的hotspot),负责 JAVA_HOME/lib下面的类库中的类的加载,这个加载器,java程序无法引用到。
- ExtClassLoader
继承BootClassLoader
扩展类加载器Extension Loader
由sun.misc.Launcher$ExtClassLoader类实现,可在java中使用,负责JAVA_HOME/lib/ext 目录和java.ext.dir目录中类库的类的加载。
- AppClassLoader
继承ExtClassLoader
应用系统类加载器Application System Loader,由sun.misc.Louncher$AppClassLoader实现,负责加载用户类路径中类库中的类,如果没有使用自定义的加载器,这个就是默认的 加载器!
- 用户自定义加载器
继承AppClassLoader - 自己定义从哪里加载类的二进制流
SecureClassLoader
UrlClassLoader
对SecureClassLoader 是继承关系,但是 此处是通过继承 对父类进行功能上的扩展
类加载器 形式上是继承关系,但功能逻辑上几乎都是 对父类的扩展, 也可以不适用继承实现扩展,可以使用包装类的思路也可以
类的来源
-
从本地文件系统加载class文件;(系统级别的 由BootClassLoader 调用,如 java.long.xxx;应用中的类由AppClassLoader 加载)
-
从Jar包加载class文件;(AppClassLoader 调用)
-
通过网络加载class文件;(UrlClassLoader 调用)
-
把一个class源文件动态编译,并执行加载。(UrlClassLoader 调用)
如何判断两个类是否是同一个类?
同一个加载器加载的同源类才是真的同类。
不同加载器加载同源类,不是同类
加载的成果
类加载的最终产品是位于堆区中的class对象,Class对象封装了类在方法区内的数据结构,并向Java程序员提供了访问方法区内的数据机构的接口.
我们可以通过类名.class来获取一个类的类型的引用,通过new 类名().getClass()来获取一个实例变量的类的引用 1
类的加载机制
双亲委派机制
当Java程序需要 加载器加载某个类时,加载器会首先委托自己的父加载器去加载该类,若父加载器能加载,则由父加载器完成加载任务,否则才由自加载器去加载。
类似 android 中 view 点击事件的传递过程
优点
- 使得java类随着它的类加载器一起具备了一种带有优先级的层次关系
- 保证Java环境的稳定性
- 避免重复加载,如果已经加载过一次Class,就不需要再次加载,而是先从缓存中直接读取。
命名空间
定义
每个类加载器都有自己的命名空间,命名空间是由该加载器以所有父加载器所加载的类组成
在同一个命名空间中不会出现类的完全限定名一样的两个类
在不同的命名空间中有可能会出现两个完全限定名一样的类
不同的类加载器的命名空间之间的关系:
- 同一命名空间内的类是相互可见的
- 子加载器的命名空间包含所有附加在其的命名空间,因此子加载器加载的类能看见父加载器加载的类,如系统加载器加载的类可以看见根加载器加载的类。
- 由父加载器加载的类对子加载器加载的类是不可见的
- 两个加载器之间没有直接或间接父子关系,则这两个加载器加载的类相互不可见
- 两个不同的命名空间内的类是项目不可见的,
但可以通过java的反射机制来访问对象的实例与方法。(android 热更新 动态加载 可以这样搞)
运行时包
定义
由同一个类加载器加载属于同包的类组成可运行时包,
决定两个类是不是同一运行时包要看它们的包名是否相同,
还要看加载器是否相同
特点
- 只有属于同一运行时包的类才能访问默认权限修饰符的类和类的成员
- 这样避免了用户自定义的类去冒充核心类库的类,
- 如自定义了一个java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库不是一个加载器加载的,它们属于不同运行时包,所以java.lang.Spy不能访问核心类的java.lang包下面的默认权限修饰符的成员