第一章:走进Java
第二章:Java内存区域与内存溢出异常
第三章:垃圾收集器与内存分配策略
第四章:虚拟机性能监控与故障处理
第五章:调优案例分析与实战
第六章:类文件结构
第七章:虚拟机类加载机制
第八章:虚拟机字节码执行引
第九章:类加载及其执行子系统的案例与实战
第十章:早期(编译器)优化
第十一章:晚期(运行期)优化
第十二章:Java内存模型与线程
第十三章:线程安全与锁优化
虚拟机把描述类的数据从class文件加载到内存,经过校验、转换解析、初始化,最形成能够被虚拟机直接使用的java类型,这就是虚拟机的加载过程
类的加载、连接、初始化在运行期进行
动态扩展的语言特性是依赖于运行期动态加载和动态连接这个特点实现的
运行期类加载应用:applet、jsp、osgi
类的加载、验证、准备和初始化顺序确定,按部就班的开始,而解析不一定:某些情况下在初始化之后,这是为了支持java语言的运行时绑定(也成动态绑定,或者晚期绑定)。互相交叉混合进行
类的加载时机没有强制约束,有且只有5种情况需要对类初始化(而加载、验证、准备自然需要在这之前)(主动引用):
遇到new、getstatic、putstatic、invokestatic四条字节码指令时候,如果没有初始化,则需要触发初始化;
常见的场景:使用new关键字实例化对象、获取或设置类的静态字段(被final修饰,在编译期把结果已经写入常量池的排除)、调用类的静态方法
使用java.lang.reflect包的方法对类进行反射调用的时候
当初始化一个类的时候,发现父类还没有初始化,则需要先初始化后父类
虚拟机启动的时候,用户需要指定一个执行的主类,虚拟机会先初始化这个主类
当使用jdk1.7的动态语言支持时,如果一个Java.lang.invoke.MethodHandle实例解析的结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这些方法句柄对应的类没有实例化,则需要先初始化
所有引用的方式都不会触发初始化,称为被动引用
在加载阶段,虚拟机需要完成3件事情
二进制流的获取
… …
一个非数组类的加载,是开发人员可控性最强的,可以自定义类加载器,定义自己的类加载器控制字节流的获取方式
数组类本身不是通过类加载器创建,是由Java虚拟机直接创建,创建数组需要遵循的规则如下:
加载阶段和连接阶段的部分内容是交叉进行的
验证的目的是为了确保class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全,防止恶意代码攻击
四个阶段的检验过程
1.文件格式验证
……
是否符合Class文件格式规范,且能够被当前版本虚拟机处理;保证输入的字节流能够被解析并存储于方法区之内,后面的三个阶段是基于方法区存储结构进行的,不会直接操作二进制流
2.元数据校验
这个类是否有父类(除Object类以外,其他类都应该有父类)
父类是否继承了不允许被继承的类(被final修饰的类)
如果类不是抽象类,是否实现了其父类或者接口中要求实现的方法
类中的字段、方法是否与父类产生矛盾(例如覆盖父类的final字段,或不符合规则的冲则)
……
对字节码的描述信息(元数据)进行语义分析,保证描述的信息符合Java语言规范要求
3.字节码校验
4.符号引用验证
… …
发生在将符号引用转换为直接引用的时候,对类信息以外的的信息进行匹配校验;
保证解析动作能够正常执行
验证阶段是非常必要的,但不是必须的
为类变量分配内存并设值初始值;变量所使用的内存都在方法区上分配
通常情况下初始为零值,如果字段属性表中存在ConstantValue属性,则被初始化ConstantValue指定的值
将常量池中的符号引用替换为直接引用的过程
符号引用:以一组符号来描述所引用的对象,符号可以是任意形式的字面量,只要使用时能无歧义的定位到目标即可,与虚拟机实现布局无关,引用的目标不一定加载到内存
直接引用:直接指向目标的指针、相对偏移量或者一个能够间接定位目标的的句柄,与虚拟机布局相关
初始化过程是执行类构造器()()的过程
clinit()方法执行过程中可能影响的程序运行特点和细节:
类加载器只用于类加载动作
对于任意一个类,都需要由加载他的类加载器和类本身确立其在Java虚拟机中的唯一性
每个类加载器,都有独立的命名空间
比较两个类”相等“,只有在同一个类加载器加载的前提下才有意义
三种系统提供的类加载器
如果没有自定义类加载器,一般情况这个就是程序默认类加载器
图中展示的类加载器的这种层次关系,称为类加载器的双亲委派模型
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都需要有自己的父类加载器
类加载的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器
加载过程:如果一个类加载收到类加载请求,先不会自己去处理,而是委托父类加载器去完成,依次递归,因此所有加载请求最终都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子类才会去尝试加载
一个显而易见的好处Java类随着类加载器一起具备了一种带有优先级的层次关系
保证java程序的稳定性
解决了各个类加载器基础类的统一问题
3次大破坏
发生在双亲委派模型出现之前,JDK1.2发布之前
自身缺陷造成(基础类调用用户代码,怎么办?SPI,Service Provider Interface加载,例如JNDI、JDBC、JCE、JAXB、JBI),线程上线文类加载器