文章目录
Jvm类加载器
一、Jvm生命周期
虚拟机的启动:
虚拟机的启动是通过引导类加载器创建一个初始类来完成的,这个类是由虚拟机的具体实现指定的
虚拟机的执行:
一个运行中的java虚拟机有着一个清晰的任务,
执行java程序程序开始执行时它才运行,程序结束时停止
执行一个所谓的java程序的时候,真真正正在执行的·是一个叫做java虚拟机的进程
退出:
正常结束
异常
System.exit();
二,类加载
类加载器只负责加载class文件,至于是否可以运行,由执行引擎来讲决定的。
加载的类信息存放在于一块称为方法区的内存空间,除了类的信息外,方法区中还会
会存放运行时常量池的信息,可能还包括字符串字面量和数字常量(这部分常量信息是
class文件中常量池部分的内存映射)。
1.类的加载过程
加载 -- (验证 -- 准备 --- 解析) --- 初始化
链接
加载:
1.通过一个类的全限定名获取此类的二进制字节流
2.将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证:
1.目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,
不会危害虚拟机自身安全。
2.主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证
准备:
1.为类变量分配内存并且设置该类静态变量的默认初始值,即零值。
2.这里不包含用fina修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化,。
如果是非显式的赋值,则会再类初始化的时候赋值。
3.这里不会实例化变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到java堆中
解析 :
1.将常量池内的符号引用转换为直接引用的过程。
2.事实上,解析操作往往会伴随着jvm在执行完初始化之后再执行。
3.符号引用就是一组符号来描述所引用的目标,符号引用的字面量形式明确定义在class文件格式中,
4.直接引用就是直接指向目标的指针,相对偏移量或一个间接定位到目标的句柄。
5.解析动作主要针对类或接口,字段,类方法,接口方法,方法类型等,
初始化:
初始化阶段就是执行类构造器方法<clinit>()的过程。带有static修饰的也会初始化,如静态方法,静态块,
此方法不需要定义,是java编译器自动收集类中的所有静态类变量的赋值动作和静态代码块中的语句合并而来。
并且给静态变量赋予初始值,如果程序对静态变量的值进行了赋值或修改,都是以代码的顺序来的,而且注意,
只能执行带有static关键字的属性和静态块
构造器方法中的指令按语句在源文件中出现的顺序执行。
<clinit>()不同于类的构造器(构造器是虚拟机视角下的<init>())。
若该类具有父类,jvm会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕。
虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁,
如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器<clinit>(),
其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出后,
其他线程在唤醒之后不会再次进入/执行<clinit>()方法,因为 在同一个类加载器下,
一个类型只会被初始化一次。
如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个线程阻塞,
在实际应用中这种阻塞往往是隐藏的
注意:带有static修饰的代码都是被clinit来执行的,因此,延申一个问题就是,
单例模式使用静态内部类是线程安全的,
2.四种验证方式
文件格式验证,元数据验证,字节码验证,符号引用验证
文件格式验证:
是要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
如验证魔数是否0xCAFEBABE;主、次版本号是否正在当前虚拟机处理范围之内;
常量池的常量中是否有不被支持的常量类型……该验证阶段的主要目的是保证输入的字节流能
正确地解析并存储于方法区中,经过这个阶段的验证后,字节流才会进入内存的方法区中存储,
所以后面的三个验证阶段都是基于方法区的存储结构进行的。
元数据认证:
是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。
可能包括的验证如:这个类是否有父类;这个类的父类是否继承了不允许被继承的类;
如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法
字节码验证:
主要工作是进行数据流和控制流分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。
如果一个类方法体的字节码没有通过字节码验证,那肯定是有问题的;
但如果一个方法体通过了字