目录
jdk(Java Development Kit //Java开发工具包)
jre(Java runtime environment //Java运行环境)
jvm(Java Virtual Machine //Java虚拟机)
一、官网眼中的JVM
二、JVM相关知识点
1.JVM是什么?
他是整合java程序多平台运行的根本,因为他屏蔽了不同操作系统的底层差异。
2.JDK,JRE,jVM 关系区别
jdk(Java Development Kit //Java开发工具包)
是包含jre与jvm的超集。JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre。
JDK是整个JAVA的核心,包括了Java运行环境JRE(Java Runtime Envirnment)、一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。
jre(Java runtime environment //Java运行环境)
是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
JRE中包含了Java virtual machine(JVM),runtime class libraries和Java application launcher,这些是运行Java程序的必要组件。
与大家熟知的JDK不同,JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器),只是针对于使用Java程序的用户。
jvm(Java Virtual Machine //Java虚拟机)
就是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先通过javac编译为.class的类文件,这种类文件可以在虚拟机上执行。
也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机(相当于中间层)间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
只有JVM还不能成class的执行,因为在解释class的时候JVM需要调用解释所需要的类库lib,而jre包含lib类库。
JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
所有的程序只要有java虚拟机的支持,那么就可以实现程序的执行,并且不同的操作系统上会有不同版本的jvm。
jvm相当于一个容器,放到不同的操作系统中,因为编写的Java程序经过编译后生成的字节码可以被JVM识别,JVM为程序运行屏蔽了底层操作系统的差异
一个java程序的运行如下图
3.javac的过程
4.类的在JVM中的加载过程
类的加载过程分为三步:加载-链接-初始化
加载:将class文件加载到内存中,通过类的权限和包名来找class文件,
链接分为三步:
验证-准备-解析 验证是验证.class二进制文件的合法性,验证class文件的开头,版本等等。
准备就是对一些类的静态变量赋初始默认值
注意举例:public static int a = 1; 针对该变量a,在该阶段,会为a分配内存,且初始化默认值。
这里不会解析到 a =1,这里只会根据数据类型来决定默认值,即int–0,double–0.0,boolean–false,object–null,等,至于执行 a = 1 的操作,是在后续的初始化阶段完成。
解析:将对应的符号引用转换为直接引用
初始化:
class的初始化过程
class的初始化时机有6种
1,类的实例创建的四种方式(new,class反射,克隆,反序列化),可看下边链接
2.访问类中的某个静态变量,或者对静态变量进行赋值
3.主动调用类的静态方法
4.Class.forName("包类名")
5.完成子类的舒适化,也会完成对本类的初始化,即先初始化父类,在找自己
6.该类是程序引导入口例如:main入口
/** * 类加载时静态你成员变量的赋值过程: * * 一:loadClass(类加载) * *
1. 将class对象加载到内存中 * *
2. 给class对象的静态成员变量赋默认值 * *
3. 给class对象的静态成员变量赋初始值 * *
二:newObject(创建对象) * *
1. 给Object对象申请内存空间 * *
2. 将Object对象加载到内存 * *
3. 给Object对象的静态成员变量赋默认值 * *
4. 给Object对象的静态成员变量赋初始值 */
public class D03ClassLoaderProcedure {
public static void main(String[] args) {
// ----- 结果为:3
System.out.println("count01 -- " + Count_01.count);
// ----- 结果为:2
System.out.println("count02 -- " + Count_02.count);
}
/** *
1. 将 Count_01 对象加载到内存 *
2. 给 count 属性赋默认值:0 *
3. 给 count_01 属性赋默认值:null *
4. 给 count 属性赋初始值:2 *
5. 给 count_01 属性对象赋初始值:new Count_01() * 注意:此时的 count 值为:2,在第 5 步给 count_01 属性赋初始值时会调用 Count_01 的构造方法 * 所以:count 会 ++,最终 count 值为:3 */
private static class Count_01 {
public static int count = 2;
public static Count_01 count_01 = new Count_01();
private Count_01() { count++; }
}
/** *
1. 将 Count_02 对象加载到内存 *
2. 给 count_02 属性赋默认值:null *
3. 给 count 属性赋默认值:0 *
4. 给 count_02 属性赋初始值:new Count_02() *
5. 给 count 属性赋默认值:2 * 注意:在第 4 步给 count_02 属性赋初始值时会调用 Count_02 的构造方法,此时的 count 还没有赋初始值,只有默认值,所以此时 count为:0,count++为:1 * 在第 5 步给count 属性赋默认值时会用 2 覆盖 count++:1 的值,所以 count 最终值为:2
*/
private static class Count_02 {
public static Count_02 count_02 = new Count_02();
public static int count = 2;
private Count_02() { count++; }
}
}
4.双亲委派机制
什么是双亲委派机制
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
双亲委派机制的作用
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
BootstrapClassLoader
称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,如java.lang.Integer等
ExtClassLoader称为扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包或者由java.ext.dirs系统属性指定的jar包.放入这个目录下的jar包对AppClassLoader加载器都是可见的(因为ExtClassLoader是AppClassLoader的父加载器,并且Java类加载器采用了委托机制).
AppClassLoader应用类加载器,又称为系统类加载器,负责在JVM启动时,加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径.
三.课后小结练习
关于JVM内容以下说法不正确的是(BD)
A JVM是Java平台的重要组成部分.负责屏蔽操作系统及硬件的差异.
B JVM是JAVA虚拟机只能运行JAVA语言的代码.
C JVM是一个虚拟的计算机器.其拥有独立的指令集和不同的运行时内存区域
D JDK是整个JAVA语言能做到 Write Once Run Anywhere的根本
以下哪个类加载器会加载java.lang.Integer(A)
A BootstrapClassLoader
B ExtClassLoader
C AppClassLoader
D CustomClassLoader
已知S extends F 类,请问以下哪个选项不会触发S类的初始化(C)
A Class.forName(“xxxx.S”)
B 调用S的sayHi的静态方法
C 调用F的sayHello的静态方法
D F s = new S();
关于类加载机制以下说法不正确的是(D)
A 双亲委派可以避免类的重复加载
B 双亲委派保护应用程序安全,防止核心环境遭受破坏
C 自定义classLoader重写loadClass方法可破坏双亲委派机制
D ClassPath环境变量下的class文件由ExtClassLoader负责加载
所以不管是什么语言,只要能编译成合法的规范的class文件,我jvm都能让其运行。