1. Java进程监控工具
jconsole ,jvisualvm
利用以上2个工具可以进行堆栈,线程等信息的查看
2. Java类加载器生命周期
正常结束,异常结束,执行 system.exit方式终止
3. 类的加载器深入解析
加载: 查找并加载类的二进制数据
连接:
- 验证: 确保被加载类的正确
- 准备: 为类的【静态变量】分配内存,并初始化为默认值
- 解析: 符号引用转换化为直接引用
初始化:为类的静态变量初始化为正确的值
使用: 主动使用、被动使用
卸载: ******
所有的Java虚拟机实现,必须在每个类或者接口被Java程序【首次主动使用】的时候才会初始化他们
4. 主动使用的情况
1. 创建类实例 ig:new object()
2. 访问某类或者接口的静态变量 取值赋值操作
3. 调用类的静态方法
4. 反射
5. 初始化子类
5. 子类和父类的加载优先级关系一
1 | public class Parent { |
i am parent static block
i am parent normal block
i am parent constructor
结论: 先初始化代码块,然后调用构造函数,其中静态代码块优先级要高于普通代码块
5. 子类和父类的加载优先级关系二
1 | public class Son extends Parent { |
i am parent static block
i am Son static block
i am parent normal block
i am parent constructor
i am Son normal block
i am Son constructor
结论:
1. 静态代码块高于一切优先级最高
2. 其次为父类的普通代码块和构造函数
3. 父类的构造函数优先于子类的普通代码块
6. 类加载概述
类的加载是指将类的二进制数据读入内存,放在运行时方法区,然后创建一个Class对象,用来封装类在方法去的数据结构
加载class文件的方式
1. 本地加载
2. 网络下载
3. 加载jar,zip等归档文件中的class文件
4. 数据库提取
5. Java源文件动态编译为class文件
7. 主动使用和被动使用
1. 对于调用静态字段,只有直接定义了该字段的类才会被初始化
1 | public class Parent { |
输出结果: i am parent static block
由此可见,只有父类的静态代码块被调用了,这这个例子当中,是对父类的主动引用,虽然调用了son类。 初始化子类,父类也会被初始化
8. 追踪被加载的类信息
使用 -XX:+TraceClassLoading 追踪类加载信息并打印
+ 表示开启 追踪,可以用 - 减号关闭
-xx:<options>=<value>表示赋值为value
9. 实例1
1 | public class Test2 { |
str 常量使用final修饰: 静态代码块会调用否则不会
在编译阶段:final修饰的常量,会被放置到调用者类的常量池当中
本质上,调用类并没有直接饮用到定义常量的类,因此不会触发定义常量的类的初始化
10. 双亲委托机制
1. 根类加载器 BootClassLoader
2. 扩展类加载器 ExtClassLoader
3. 系统类加载器 AppClassLoader
在双亲委托机制中,各个了类加载器按照父子关系形成树形结构,
除了根类加载器以后,其他类加载器都有且只有一个父亲
11. 继承类的主动使用
结论 通过子类对于父类静态变量和静态函数的调用,并不属于对子类的主动使用,
所以,子类的静态代码块没有执行
看的是定义方,而不是调用方
12. 类的卸载
1. 由Java虚拟机自带的类加载器(根类加载器,扩展类加载器,系统来加载器)加载的类,
在jvm生命周期内,始终不会被卸载。
2. 由用户自定义的类加载器加载的类,是可以被卸载的。
13. 追踪被卸载的类信息
使用 -XX:+TraceClassUnloading 追踪类加载信息并打印
14. 复杂类的加载
对于父加载器加载的类,对于子加载器是可见的,但是
对于子加载器加载的类,对于父加载器是不可见的
15. 命名空间的概念
1. 每个类加载器都有一个命名空间,
2. 子加载器包含所有父类的命名空间可以访问父加载器加载的类,
3. 父加载器锁加载的类,无法被子加载器访问
4. 在同一个命名空间内,不会出现同一个类名和包名相同的类
5. 同一个命名空间内,类之间是相互可见的,反之不可见
16. 启动类加载器
内建于jvm的启动类加载器会加载ClassLoader以及其他的Java平台类
当jvm启动时,一块特殊的代码块会运行,他会加载扩展类加载器以及系统类加载器,
启动类加载器是c++实现,特定平台的机器指令,负责开启整个加载过程。
启动类加载器负责加载JRE基本组件,java.util java.lang包中的类
17. 线程上下文类加载器
破坏双亲委托机制,使得我们可以让某一个加载器去加载类
1. 当前类加载器 (currentClassLoader):
用于加载当前类的加载器
每个类都会使用自己的类加载器去加载其他类,如果classX引用
了classY,那么classX的类加载器就会去加载classY(如果
classY尚未被加载)
2. 线程上下文类加载器
jdk 1.2 引入的,thread类 getContextClassLoader方法
如果没有通过setContextClassLoader设置,线程将继承
父类的上下文加载器,
Java应用运行时的初始线程的上下文加载器是系统类加载器,
在线程中运行的代码可以通过该类加载器来加载类与资源
父classLoader可以使用当前线程
Thread.currentClassLoader().getClassLoader 所指定的
classloader加载的类,这就改变了父classloader不能使用子
classloader或者没有父子关系的classloader加载的类的情况。
例如 jdbc规范的实现
高层提供统一的接口让低层去实现,同时又要在高层使用低层的类
18. jar hell问题解决
当某各类存在多个jar包中,我们可以通过以下方式寻找解决
19. 类的加载过程解析
1. 加载
2. 连接 - 验证-准备-解析
3. 初始化
4. 使用
5. 卸载
1. 加载
在类加载过程中,虚拟机主要完成以下事件
1. 获取二进制流
2. 将二进制流转化为方法区的运行时数据结构
3. 内存中生成代表这个类的Class对象,作为访问各种数据的入口
2. 验证
验证主要是为了确保Class文件中的字节流符合虚拟机的要求,不会危及虚拟机安全。
1. 文件格式验证
检查魔数,主此版本号看是否虚拟机可以支持,常量池检查
2. 元数据验证
对语义进行分析,看是否符合Java规范。例如:
继承了final类,抽象类方法未实现
3. 字节码验证
4. 符号引用验证
3. 准备
准备阶段会为类的静态变量分配内存并初始化默认的零值
4. 解析
符号引用转化为直接引用
5. 初始化
为静态变量初始化正确的赋值
3. 类加载器
4. 双亲委托
1. 工作过程
如果一个类加载器收到了类加载的请求,它首先会去请求父类加载器去完成,
因此所有的加载请求最终都会传送到顶层的类加载器中。
只有父类加载器反馈自己无法完成类加载请求,子类加载器才会去
尝试加载。
2. 双亲委托的好处
保持层级关系
保证在当前类加载上下文中,类的唯一性
5. 双亲委托模型的破坏
1. 双亲委托模型是jdk1.2开始的,so
2. 线程上下文加载器
3. 程序动态性
1. 线程上下文改变了双亲委托模型
父classLoader可以使用当前线程
Thread.currentClassLoader().getClassLoader 所指定的
classloader加载的类,这就改变了父classloader不能使用子
classloader或者没有父子关系的classloader加载的类的情况。
jdbc 的实现