JVM体系结构
- JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。 - JVM体系结构简图:
JVM是介于java程序和操作系统之间的一个区域,由于是由C++编写,因此java又可以称为C++ - -,一个java文件编写完成后会被编译成class文件,然后经过类加载器进入运行时数据区。
由于main方法在运行时会被压入栈底,每运行完一个方法会被弹出,因此垃圾回收不可能存在在栈里,而应该存在于堆中,由于方法区为特殊的堆,因此大部分性能调优都是对于堆的性能调优。
类加载器
类加载器的主要作用是加载类对象,将class文件加载为class类,如图所示:
car.class
通过new的方式可以将class对象实例化为不同的实例化对象,实例化对象可以通过getClass的方式反射获得类对象,因此可以得出类是模板,而对象是具体的,所有的对象都是由同一个类实例化的。类的hashcode恒定,而每个对象各不相同。
测试
public class Car {
public static void main(String[] args) {
//类是模板,对象是具体的
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
System.out.println(car1.hashCode());
System.out.println(car2.hashCode());
System.out.println(car3.hashCode());
Class<? extends Car> car1Class = car1.getClass();
Class<? extends Car> car2Class = car2.getClass();
Class<? extends Car> car3Class = car3.getClass();
System.out.println(car1Class.hashCode());
System.out.println(car2Class.hashCode());
System.out.println(car3Class.hashCode());
}
}
测试结果
类加载器的种类
- 虚拟机自带的加载器
- 启动类(根)加载器:负责加载支撑JVM运行的位于jre/lib目录下的核心类库(例如:String、Object类),在虚拟机启动时就会加载完,以支撑虚拟机的运行。
- 扩展类加载器:负责加载支撑JVM运行的位于jre/lib/ext中的JAR包。由Java语言实现,父类加载器为null。
- 应用程序加载器:负责加载用户路径ClassPath下的类库。由Java语言实现,父类加载器为ExtClassLoader。
类加载器加载Class大致要经过如下8个步骤:
- 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
- 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
- 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
- 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
- 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
- 从文件中载入Class,成功后跳至第8步。
- 抛出ClassNotFountException异常。
- 返回对应的java.lang.Class对象。
类加载机制
- 双亲委派机制
双亲委派机制指的是类加载器(AppClassLoader)收到类加载的请求,会将这个请求向上委托给父类加载器去完成,一直向上委托。
应用程序加载器-------扩展类加载器-------根加载器-------启动类加载器
启动类加载器会检查是否可以加载这个类,如果可以加载就结束,使用当前加载器,否则抛出异常,通知子加载器进行加载,我们平时自定义的方法都是运行在应用程序加载器(AppClassLoader)上面的。
启动类加载器-------根加载器-------扩展类加载器-------应用程序加载器
当所有加载器都无法加载会通过native调用操作系统层的本地方法。 - 双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。