JVM指令集、类加载子系统介绍
Jvm指令集架构:
指令集架构分为两种:
1.基于栈的指令集架构(Java编译器是基于栈的指令集架构)。
2.基于寄存器指令架构。
栈的指令架构:
优势:跨平台、零地址指令、指令集更小、移植性高。(编译器更容易实现)
劣势:指令多,性能下降(实现同样的功能需要更多指令)
寄存器指令架构:
优势:性能优秀、执行效率高,指令少
劣势:移植性差
Jvm生命周期:
跟随线程的结束或终止生命周期就结束(System.exit()方法或Runtime类的Halt方法)。
类加载子系统作用:
类加载子系统负责从文件系统或网络中加载class文件,class文件在文件开头有特定文件标识(Ca Fe Ba Be)。ClassLoad只负责class文件的加载,是否可以运行由Execution Engine决定。
Jvm加载器:
1.启动类加载器(引导类加载器 Bootstrap ClassLoader):
1) 使用C/C++语言实现,嵌套在Jvm内部。
2) 用来加载Java核心类库。
3) 加载扩展类和系统类,指定为它们的父类加载器。
2.扩展类加载器(Extension ClassLoader):
1) Java语言编写。
2) 派生于ClassLoader类。
3) 从java.ext.dirs系统属性所指定目录中加载或JDK的安装目录jre/lib/ext子目录下加载。
4) 如果用户创建的Jar放在jre/lib/ext子目录下,自动由扩展类加载器加载。
3.系统类加载器(App ClassLoader):
1) Java语言编写。
2) 派生于ClassLoader类。
3) 父类为扩展类加载器。
4) 负责加载环境变量classpath或系统属性java.class.path指定下的类库。
5) Java应用类,自定义类都是由系统加载器完成加载。
自定义加载器的好处:
1) 隔离加载类 2) 修改类的加载方式 3) 扩展加载源 4) 防止源码泄漏
如何自定义加载器:
1) 通过继承抽象类java.lang.ClassLoader的方式,实现自己的类加载器。
2) 如果没有复杂的需求,可以直接继承URLClassLoader类,可以避免编写findClass()方法及获取字节码的方式,使自定义加载器更加简洁。
/**
* 自定义类加载器
*/
public class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath){
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) {
byte[] classByte = loadClassByte(name);
return defineClass(name, classByte, 0, classByte.length);
}
private byte[] loadClassByte(String name) {
// 获取该类在文件系统中保存的格式 (即路径加文件名)
String fileName = classPath + File.separator + name.replace(".",File.separator) +".class";
File file = new File(fileName);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try(InputStream is = new FileInputStream(file)){
int len;
byte[] b = new byte[1024*8];
while ((len = is.read(b)) != -1) {
outputStream.write(b, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
}
public static void main(String[] args) throws Exception {
//路径请使用classpath外的一个class文件,并且类路径下不能包含此class,才能使用到自定义的类加载器
MyClassLoader myClassLoader = new MyClassLoader("F:\\chenxi");
Class clazz = myClassLoader.loadClass("com.MyClassLoading");
System.out.println(clazz + "-" + clazz.newInstance()+ "-" +clazz.getClassLoader());
}
类加载过程:
分为三个阶段:1.加载 2.链接 3.初始化
加载阶段:
1.通过一个类的全限定名获取定义此类的二进制字节流。
2.将字节流所代表的静态存储结构化为方法区的运行时数据结构。
3.在内存中生成一个代表这个类的java.lang.class对象作为一个方法区,这个类的各种数据访问入口。
加载.class文件的方式:
1.Jar,War格式的基础,从zip压缩包中读取
2.动态代理技术,运行时计算生成。
链接阶段:
1.验证:确保class文件中的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全(四种验证:1.文件格式 2.元数据 3.字节码 4.符号引用)。
2.准备:为变量分配内存并设置该类变量的默认初始值(零值)(不包含用final修饰的static,因为final在编译时就分配了,准备阶段会显式初始化)。
3.解析:将常量池内的符号引用转换为直接引用的过程,解析操作随着Jvm在执行空初始化后再执行(解析主要针对于:类、接口、字段、类方法、接口方法、方法类型等)。
初始化阶段:
执行类构造器的方法
static int a = 1;
static{
a = 2;
}
构造器方法中指令按语句在源码文件中出现的顺序执行。