1.JVM内存结构
、
由上图可知,类加载过程:加载--->链接--->实例化
如果想手写一个Java虚拟机,类加载子系统和执行引擎必须。
2.类的加载过程
类加载只是将对应的字节码文件加载到内存之中,能不能执行还是得看执行引擎
1.loading
根据全限定名获取字节的二进制流,将其中的静态存储结构转化为方法去的数据,生成class作为获取方法区数据的入口
加载的类的来源
- 从本地文件直接获取
- 从网络上获取
- 通过jar包之类的获取
- 运行时计算生成,动态代理技术
Linking
- 判断字节码有没有错
- 将其中的一些数据,常量数据初始化
- 解析,将一些符号解析为操作
init
- 执行类构造器方法clinit()。
- 这个是javac编译器自带的,就是把类中所有的
静态初始化方法(包括静态代码块里的)合并执行, - 顺序就是代码写的顺序。
- clinit在多线程时会被加锁,保证只加载一次(加载一次,把数据放入方法区)
在这个案例里,number在linking阶段就赋值为0了,所以在这里先变成20,再覆盖为10
几种类加载器
1.启动类加载器(引导类加载器)
- 使用c++写的,参套在jvm内部
- 用来加载jvm里面的核心类库,本身没有父类
- 因此加载扩展类加载器那些并指定他们的父加载器
- 识别Java,Javax,sun开头的这些
2.扩展类加载器
- 用Java写的
- 加载的是除了Java核心的类库之外用户扩展引入的类
3.系统类加载器
- 用Java写的
- 加载扩展类加载器加载出的那些类。(一个核心类库里会有很多的类,系统类就是加载具体的那些类)
4.用户自定义类加载器
一般用上面三种就够了,有时候需要自定义类加载器:
什么时候需要
- 隔离加载类,有时候需要一个类与其他的类之间不产生联系和冲突
- 修改类的加载方式
双亲委派机制
- 一个类加载器收到了类加载请求,他会向父类加载器委托,一直到启动类加载器
- 当父类加载器发现这个包不归自己管的时候就向下分派,一直到能加载为止
疑问:
1.为什么是双亲?
双亲应该是站在系统类加载器的角度,他有两个父类
2.为什么收到加载请求后,不一开始就直接委托给启动类,而是一层一层
默认的时系统类加载器,这样设计我感觉时因为使用递归去调,可以记录下这一层是什么加载器,一开始直接用引导类,他就不知道该分派给谁,所以就默认为系统类加载器。
3.为什么一定要传到引导类加载器
安全性问题吧,如果在这一层和父类那一层都有相同的类的话,可能会影响父类加载器里的一些库的实现,造成安全性问题。(比如Java核心类库都在引导类,但是如果在自定义类创建了一个类名相同的,不去委派到引导类加载器,就会影响实现(这一部分就是沙箱安全机制))
其他
除了启动类加载器之外,其他的类加载器除了将数据保存在方法区,还会把这个类加载器的一个引用保存在方法区,去表示我引用的是这个类加载器,做一个区分