1.类文件结构
1.1 每个class文件头的4个字节,为魔数magic number,作用:确定这个文件是否能被虚拟机接受的class文件。
基于安全的考虑。0xCAFEBABE
1.2 紧接着魔数的4个字节存储的是class文件的版本号:第5,第6个字节是次版本号(minor version),
第7,第8个字节是主版本号(major version)用于判断虚拟机是否支持该版本。
1.3 紧接着主次版本号之后的是常量池入口,放置一项u2类型的数据
1.4 在常量池结束之后,紧接着的两个字节代表访问标志
类索引,父类索引,接口索引集合,字段表集合,方法表集合,属性表集合
2.虚拟机类加载机制
类的生命周期
加载(loading),验证(verification),准备(preparation),解析(resolution),
初始化(initialization),使用(using),卸载(Unloading)
当一个类在初始化时,要求其父类全部都已经初始化过了;但是一个接口在初始化时,并不要求其父接口全部都完成初始化,
只有在真正使用到父接口的时候(如引用接口常量),才会初始化。
2.1类的加载过程
加载,验证,准备,解析,初始化
加载
1.通过一个类的全限定名来获取定义此类的二进制字节流
2.将这个字节流所代表的的静态存储结构转化为方法区的运行时数据
3.在Java堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
验证
确保Class文件的字节流中包含的信息符合虚拟机的要求
文件格式验证,元数据验证,字节码验证,符号引用验证
1.文件格式验证:
是否以魔数0xCAFEBABE开头,
主次版本号是否在当前虚拟机处理范围内
常量池是否有不被支持的常量类型
2.元数据验证
第二阶段是对字节码描述的信息进行语义分析,以保证其扫描的信息符合Java语言规范
是否有父类
父类是否继承了不允许继承的类(final)(是否继承了final类)
如果这个类不是抽象类,是否实现了父类或者接口中要求实现的所有方法
类中的字段,方法是否与父类产生了矛盾
语义校验
3.字节码验证
验证中最复杂的一个阶段,主要是通过数据流和控制流分析,程序语义合法,符合逻辑。
4.符号引用验证
符号引用中通过字符串的全限定名是否找到对应的类,
在指定类中是否存在符合方法的字段,访问的方法,字段是否存在
准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
仅包括static修饰的变量。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
符号引用:一组符号来描述引用的目标
直接引用:直接指向目标的指针
1.类或接口的解析
2.字段解析
3.类方法解析
Java.lang.NoSuchFieldError错误可能在什么阶段抛出(在符号引用验证阶段)
初始化
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法完毕。其它线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
2.2类加载器
启动类加载器(Boostrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;
另一部分就是其它的类加载器,都Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader.
绝大部分java程序都会使用到以下3种系统提供的类加载器。
启动类加载器(Boostrap ClassLoader):负责将存放在<JAVA_HOME>\lib目录中的类加载到虚拟机内存中;
扩展类加载器(Extension ClassLoader):这个加载器加载<JAVA_HOME>\lib\ext目录中的类库;
应用程序类加载器(Application ClassLoader):系统类加载器,负责加载ClassPath上所指定的类库。
双亲委派模型
双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给
父类加载器去完成,每一个层次的类加载器都是这样,因此所有的请求都应该传送到顶层的启动类加载器中,只有当父类反馈自
己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
双亲委派模型出于安全考虑,如果java.lang.Object都可以自由定义类的加载器来加载,那系统会出现多个不同的Object类,那Java类型体系中最基础的行为就无法保证,应用程序就会变得混乱。
类的加载顺序是自顶向下的,而类的检查自己是否已经被加载过,查找顺序是自底向上的。
package com.jvm.classLoader;
/**
* 自底向上检查类是否加载,
* 自顶向下尝试加载类
* Created by chenbin on 2019\9\23 0023.
*/
public class SysClassLoader {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override
protected synchronized Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
//首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name,false);
} else {
c = findBoostrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//如果父类加载器抛出ClassNotFoundException
//说明父类加载器无法完成加载请求
}
if (c == null) {
/**
* 在父类加载器无法加载的时候
* 再调用本身的findClass方法来进行类加载
*/
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
};
}
}
3.虚拟机字节码执行引擎
3.1 栈帧(stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。
栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应
着一个栈帧在虚拟机里面从入栈到出栈的过程。
局部变量表:用于存放方法参数和方法内部定义的局部变量。
操作数栈:后进先出
动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。Class文件的常量池中有大量的符号引用,这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为
直接引用,称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
方法返回地址:在方法退出之后,都需要返回到方法被调用的位置
return,正常结束返回
throw,异常退出。
3.2 方法调用
解析:Class文件的常量池中有大量的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用。
分派:静态单分派,静态多分派,动态单分派,动态多分派
重载方法解析,对父类方法覆写。
package com.jvm.overload;
/**
* 单分派,多分派演示
* Created by chenbin on 2019\9\24 0024.
*/
public class Dispatch {
static class QQ {}
static class _360{}
public static class Father {
public void hardChoice(QQ arg) {
System.out.println("Father choose qq.");
}
public void hardChoice(_360 arg) {
System.out.println("Father choose 360.");
}
}
public static class Son extends Father {
public void hardChoice(QQ arg) {
System.out.println("Son choose qq.");
}
public void hardChoice(_360 arg) {
System.out.println("Son choose 360.");
}
}
public static void main(String[] args) {
Father father = new Father();
Father son = new Son();
father.hardChoice(new _360());
son.hardChoice(new QQ());
}
}
package com.jvm.overload;
/**
* 动态分派
* Created by chenbin on 2019\9\24 0024.
*/
public class DynamicDispatch {
static abstract class Human {
protected abstract void sayHello();
}
static class Man extends Human {
@Override
protected void sayHello() {
System.out.println("man say hello.");
}
}
static class Woman extends Human {
@Override
protected void sayHello() {
System.out.println("woman say hello");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}
package com.jvm.overload;
import java.io.Serializable;
/**
* 方法重载对入参的路由
* 慎用重载
* Created by chenbin on 2019\9\24 0024.
*/
public class Overload {
public static void sayHello(Object arg) {
System.out.println("hello Object");
}
public static void sayHello(int arg) {
System.out.println("hello int");
}
public static void sayHello(long arg) {
System.out.println("hello long");
}
public static void sayHello(Character arg) {
System.out.println("hello Character");
}
public static void sayHello(char arg) {
System.out.println("hello char");
}
public static void sayHello(char...chars) {
System.out.println("hello chars...");
}
public static void sayHello(Serializable arg) {
System.out.println("hello Serializable");
}
public static void main(String[] args) {
/**
* char->int->long->float->double->Character->
* Serializable->Object->char....
*/
sayHello('a');
}
}
package com.jvm.overload;
/**
* 重载方法解析
* Created by chenbin on 2019\9\24 0024.
*/
public class StaticDispatch {
static abstract class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
public void sayHello(Human guy) {
System.out.println("hello,guy.");
}
public void sayHello(Man guy) {
System.out.println("hello,gentleman.");
}
public void sayHello(Woman guy) {
System.out.println("hello,lady.");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
//Human类型,会匹配到超类,而不会匹配到最精确类型
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);
sr.sayHello(woman);
Man man2 = new Man();
Woman woman2 = new Woman();
sr.sayHello(man2);
sr.sayHello(woman2);
}
}
备注:本文的代码可以在git中下载地址:https://github.com/chenbin911029/mutiThread.git
代码目录在:package com.jvm 中
参考文献:JVM高级特性与最佳实践(第二版)