JVM是java虚拟机的缩写,本质上是一个程序,能识别.class字节码文件(.java文件编译后产生的二进制代码),并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。
关于java语言的跨平台性(一次编译,多次运行),就是应为JVM,可以把它想象出一个抽象层,运行在操作系统之上的,与硬件没有直接的交互,只要这个抽象层JVM正确执行了.class文件,就能运行在各种操作系统之上了。
介绍几个术语:
JDK:java开发工具包,JDK=JRE+javac/java/jar等指令工具
JRE:java运行环境,JRE=JVM+java基本类库
JVM体系结构 java虚拟机主要分为五大模块:
类加载器
运行时数据区
执行引擎
本地方法接口
垃圾收集模块
方法区是一种特殊的堆,栈里面不回有垃圾,用完就弹出了,否则阻塞了main方法。垃圾几乎都在堆里,所以JVM性能调优%99都针对与堆。
目前最常用的JVM是Sun公司的HotSpot,此外还有BEA公司的JRockit和IBM公司的J9 VM。
类加载器 作用:加载.class字节码文件。
new一个对象的过程 //运行时,JVM将Test的信息放入方法区 public class Test{ public static void main(String[] args){ Student s1 = new Student("Tom");//引用放在栈里,具体的实例放在堆里 Student s2 = new Student("Jerry"); Student s3 = new Student("Victor"); //三个hashCode是不同的,因为是三个不同的对象,对象是具体的 System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println(s3.hashCode()); //class1,class2,class3为同一个对象,因为这是类模版,模版是抽象的 Class<? extends Stedent> class1 = s1.getClass(); Class<? extends Stedent> class2 = s2.getClass(); Class<? extends Stedent> class3 = s3.getClass(); System.out.println(class1.hashCode()); System.out.println(class2.hashCode()); System.out.println(class3.hashCode()); } }首先Class Loader读取字节码文件,加载初始化生成Student模版类。
通过Student模版类new出三个对象。
类加载器的类别 public class Test{ public static void main(String[] args){ Student s = new Student("Tom"); Class<? extends Student> c = s.getClass(); ClassLoader classLoader = c.getClassLoader(); System.out.println(classLoader);//APPClassLoader System.out.println(classLoader.getParent());//PlatformClassLoader System.out.println(classzLader.getParent().getParent());//null,获取不到(C++写的) } } 根据返回结果,级别从高到低有三种加载器:
启动类(根)加载器:BootStrapClassLoader。
c++编写的,加载java核心库,构造拓展类加载器和应用程序加载器
根加载器加载拓展类加载器,并且将拓展类加载器的父加载器设置为根加载器
然后在加载应用程序加载器,应将应用程序的加载器的父加载器设置为拓展类加载器
由于根加载器涉及到虚拟机本地实现的细节,我们无法直接获取到启动类加载器的引用,这就是上面第三个结果为null的原因
加载文件存在于/jdk/jdk1.8/jre/lib/rt.jar
拓展类加载器:PlatformClassLoader
java编写,加载扩展库,开发者可以直接使用标准扩展类加载器
java9之前称为ExtClassLoader
加载文件存在于.../lib/ext
应用程序加载器:AppClassLoader
Java编写,加载程序所在的目录,是java默认的类加载器
用户自定义加载器:CustomeClassLoader
java编写,用户自定义的类加载器,可加载指定路径的class文件
实际上,这些加载器的区别就是加载不同范围或不同路径的.class文件。
双亲委派机制 双亲委派机制是类加载器收到类加载的请求,会将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器BootStrapClassLoader。根加载器检查是否能够加载当前类,能加载就结束,使用当前类加载器,否则就抛出异常,通知子加载器进行加载。
举个例子,我们重写java.lang包下的String类:
package java.lang; public class String{ public String toString(){ return "xing"; } public static void main(String[] args){ new String().toString; } } //Error:(1,1) java:程序包已存在于另一个模块中:java:base 我们会发现报错,这就是双亲委派机制起的作用,当类加载器委托到根加载器的时候,String类已经被根加载器加载过一遍了,所以不会再加载,从一定程度上防止了危险代码的植入。
作用总结:
防止重复加载同一个.class,通过不断委托父加载器直到根加载器,如果父加载器加载过了,就不用再加载一遍,保证数据安全。